In einer Nussschale
Da ich die Literatur nicht genug kannte, erarbeitete ich eine Lösung, die im nächsten Abschnitt vorgestellt wird, zusammen mit einem Beweis für den schwierigsten Teil. Sobald ich wusste, was gebraucht wurde, konnte ich in der Literatur nach den richtigen Ideen suchen. Hier ist eine kurze Darstellung des Algorithmus, basierend auf der Literatur, die im Wesentlichen dieselbe ist, die ich entwickelt habe.
Das erste, was Sie tun müssen, ist, eine Terminalzeichenfolge mit minimaler Größe zu finden
σ(U) für jedes Nicht-Terminal Uder Grammatik. Dies kann erreicht werden, indem Knuths Erweiterung auf Conc- oder Graphen (auch als CF-Grammatiken bezeichnet) und - oder Graphen des Dijkstra-Algorithmus für kürzeste Wege verwendet wird . Beispiel B in Knuths Artikel macht fast das, was benötigt wird.
Eigentlich berechnet Knuth nur die Länge dieser Terminalzeichenfolgen, aber es ist ziemlich einfach, seinen Algorithmus so zu ändern, dass tatsächlich eine solche Terminalzeichenfolge berechnet wird σ(U) für jedes Nicht-Terminal U(wie ich es in meiner eigenen Version unten mache). Wir definieren auchσ(a)=a für jedes Terminal aund wir verlängern σ wie üblich in einen String-Homomorphismus.
Dann betrachten wir einen gerichteten Graphen, in dem Nicht-Terminals die Knoten sind und es einen Bogen gibt (U,V) Wenn es eine Regel gibt U→αVβ. Wenn mehrere solcher Regeln den gleichen Bogen erzeugen können(U,V)Wir behalten eine so, dass die Länge |σ(αβ)|ist minimal. Der Bogen ist mit dieser Regel und dieser minimalen Länge gekennzeichnet
|σ(αβ)| wird das Gewicht des Bogens.
Schließlich berechnen wir unter Verwendung des Dijkstra-Algorithmus für den kürzesten Pfad den kürzesten Pfad aus dem anfänglichen Nicht-Terminal Szu jedem Nicht-Terminal der Grammatik. Gegeben der kürzeste Weg für ein Nicht-TerminalUkönnen die Regelbezeichnungen auf den Bögen verwendet werden, um eine Ableitung zu erhalten
S⟹∗αUβ. Dann zu jeder Regel des FormularsU→γ In der Grammatik ordnen wir die Terminal-Zeichenfolge mit minimaler Größe zu σ(αγβ) die mit dieser Regel abgeleitet werden können.
Um eine geringe Komplexität zu erreichen , werden sowohl der Dijkstra-Algorithmus als auch die Knuth-Erweiterung mit Haufen und AKA-Prioritätswarteschlangen implementiert . Dies gibt für den Dijkstra-Algorithmus eine Komplexität vonO(nlogn+t)und für Knuths Algorithmus eine Komplexität O(mlogn+t), wo sind sie m
Grammatikregeln und n Nicht-Terminals und tist die Gesamtlänge aller Regeln. Das Ganze wird seitdem von der Komplexität des Knuth-Algorithmus dominiertm≥n.
Was folgt, ist meine eigene Arbeit, bevor ich die kurze Antwort oben produzierte.
Ableiten der Lösung aus dem Algorithmus zur Beseitigung nutzloser Symbole.
Dieser Algorithmus hat mehrere Aspekte. Zur besseren Intuition habe ich mich entschieden, es in drei aufeinander folgenden Versionen zu präsentieren, die zunehmend mehr Funktionen einführen. Die erste Version beantwortet die Frage nicht, ist jedoch ein Standardalgorithmus für die Beseitigung nutzloser Symbole, der eine Lösung vorschlägt. Die zweite Version beantwortet die Frage ohne die Minimalitätsbeschränkung. Die dritte Version gibt eine Antwort auf die Frage und erfüllt die Minimalitätsbeschränkung. Diese dritte Lösung wird dann verbessert, indem eine Anpassung an und / oder Diagramme des Dijkstra-Algorithmus für kürzeste Wege verwendet wird .
Das Endergebnis ist ein sehr einfacher Algorithmus, der es vermeidet, bereits durchgeführte Berechnungen zu überdenken. Aber es ist weniger intuitiv und erfordert einen Beweis.
Diese Antwort versucht nur, die Frage zu beantworten, die durch den Kommentar des OP präzisiert wurde: " Für jede Produktionsregel möchte ich eine minimale Zeichenfolge generieren, die den Parser vom Startzustand über die getestete Produktion zu einer Reihe von Terminals führt. "Daher versuche ich nur, eine Reihe von Zeichenfolgen zu erhalten, sodass für jede Regel eine Zeichenfolge in der Gruppe vorhanden ist, die eine der Zeichenfolgen mit minimaler Größe der Sprache ist, die eine Ableitung unter Verwendung der Regel aufweist.
Es muss jedoch beachtet werden, dass die Tatsache, dass eine Zeichenfolge eine Regel "aufruft", dh eine Ableitung unter Verwendung dieser Regel hat, nicht unbedingt bedeutet, dass die Regel von einem Parser berücksichtigt wird, der mit mehrdeutigen Grammatiken arbeitet und Mehrdeutigkeiten willkürlich auflöst. Die Behandlung einer solchen Situation würde wahrscheinlich genauere Kenntnisse des Parsers erfordern und könnte eine komplexere Frage sein.
Der grundlegende Algorithmus
Um diese Frage zu lösen, kann man mit dem klassischen Algorithmus zum Entfernen nutzloser Symbole in kontextfreien Grammatiken beginnen. Es befindet sich in Abschnitt 4.4, S. 88-89, von Hopcroft & Ullman, Ausgabe 1979. Aber die Präsentation hier kann etwas anders sein.
Der Algorithmus zielt genau darauf ab, das Vorhandensein einer solchen Abdeckung zu beweisen, wie vom OP gefordert, und besteht aus zwei Teilen:
Lemma 4.1 von H & U, Seite 88: Entfernen aller unproduktiven Nicht-Terminals . Dazu wird versucht, für jedes Terminal eine Terminalzeichenfolge zu finden, von der es abgeleitet werden kann. Eine einfache Möglichkeit, dies zu erklären, lautet wie folgt: Sie erstellen eine MengeProdod produktive Symbole, die Sie mit allen Terminals initialisieren. Dann enthält jede noch nicht verarbeitete Regel alle rechtsseitigen Symbole (RHS)ProdFügen Sie dem Set das linke Terminal (LHS) hinzu Prodund Sie entfernen alle Regeln mit demselben LHS-Nicht-Terminal aus dem zu verarbeitenden Regelsatz. Sie wiederholen den Vorgang, bis keine Regel mehr mit allen RHS-Symbolen vorhanden istProd. Die restlichen Nicht-Terminals, nicht inProd am Ende dieses Prozesses sind sie nicht produktiv: Sie können nicht in eine Terminalzeichenfolge abgeleitet und somit aus der Grammatik entfernt werden.
Lemma 4.2 von H & U, Seite 89: Entfernen aller nicht erreichbaren Symbole . Dies geschieht durch die klassische Erreichbarkeit von Knoten in gerichteten Graphen, indem Nicht-Terminals als Knoten betrachtet werden und ein Bogen vorliegt(U,V) Wenn es eine Regel gibt U→α so dass
V tritt auf in α. Sie erstellen ein SetReach von erreichbaren Symbolen, die nur mit dem Anfangssymbol initialisiert werden S. Dann für jedes nicht-terminale SymbolU im Reach oder später hinzugefügt, und für jede Regel U→α, fügst du hinzu Reach alle Symbole in α. Wenn alle Nicht-Terminals inReach Auf diese Weise wurden alle Symbole (Terminal oder Nicht-Terminals) verarbeitet, die nicht in enthalten sind Reachkann nicht in einer vom Anfangssymbol abgeleiteten Zeichenfolge erscheinen und ist daher unbrauchbar. Somit können sie aus der Grammatik entfernt werden.
Diese beiden grundlegenden Algorithmen sind nützlich, um die Rohergebnisse einiger Grammatikkonstruktionstechniken zu vereinfachen, wie sie beispielsweise für die Überschneidung einer kontextfreien Sprache und einer regulären Menge verwendet werden. Dies ist insbesondere nützlich, um die Ergebnisse allgemeiner CF-Parser zu bereinigen .
Das Entfernen von nicht-terminalen Symbolen ist im Zusammenhang mit der Lösung der gestellten Frage erforderlich, da die Regeln, die sie verwenden, von keiner Zeichenfolge der Sprache "aufgerufen" (dh in ihrer Ableitung verwendet) werden können.
Erstellen eines Satzes von Zeichenfolgen, die jede Regel aufrufen
(Wir suchen noch nicht nach minimalen Zeichenfolgen.)
Wenn man nun speziell die Frage beantwortet, muss man tatsächlich alle nutzlosen Symbole entfernen, ob nicht erreichbare Symbole oder unproduktive nicht-terminale Symbole, sowie nutzlose Regeln mit solchen nutzlosen nicht-terminalen Symbolen wie LHS. Sie haben keine Chance, beim Parsen einer Terminalzeichenfolge jemals sinnvoll aufgerufen zu werden (obwohl einige möglicherweise die Verarbeitungszeit eines Parsers verschwenden, wenn sie nicht entfernt werden; welche Zeit verschwenden können, hängt von der Parsertechnologie ab).
Wir betrachten nun für jede (nützliche) Regel die Erzeugung einer Terminalzeichenfolge, die sie aufruft, dh die unter Verwendung dieser Regel erzeugt werden kann. Dies ist im Wesentlichen das, was diese beiden oben genannten Algorithmen tun, obwohl sie die Informationen nicht speichern, da sie damit zufrieden sind, die Existenz dieser Zeichenfolgen zu beweisen, um sicherzustellen, dass Nicht-Terminals sowohl erreichbar als auch produktiv sind.
Wir modifizieren den ersten Algorithmus (Lemma 4.1), indem wir uns an jedes Nicht-Terminal halten U im Set Prod eine Terminalzeichenfolge σ(U) es leitet sich ab auf: U⟹∗σ(U). Für jedes Terminal definieren wir dieσals Identitätszuordnung. WannU wird dem Set hinzugefügt Prod weil eine Regel
U→γ hat alle seine RHS-Symbole in Prod, dann definieren wir
σ(U)=σ(γ), verlängern σ als Homomorphismus auf Strings, und wir entfernen alle U-Regeln, das sind alles Regeln mit U als LHS.
Wir modifizieren den zweiten Algorithmus (Lemma 4.2), indem wir jedes nicht-terminale Symbol beibehalten U hinzugefügt zu Reach der Pfad, über den es vom Anfangssymbol aus erreicht wurde S, was die aufeinanderfolgenden Regeln gibt, um eine Ableitung zu erhalten S⟹∗αUβ.
Dann für jede Regel U→γIn der Grammatik erzeugen wir eine Terminalzeichenfolge, die diese Regel wie folgt "aufruft". Wir nehmen aus dem Ergebnis des zweiten Algorithmus die Ableitung
S⟹∗αUβ. Dann wenden wir die Regel an, um den String zu erhaltenαγβ. Eine Terminalzeichenfolge, die die Regel "aufruft"U→γ ist σ(αγβ)
Erstellen einer Reihe minimaler Zeichenfolgen, die jede Regel "aufrufen"
Wir ignorieren das Problem der Beseitigung nutzloser Symbole, die ein Nebenprodukt dieser modifizierten Algorithmen sein können.
Das Erstellen eines Satzes minimaler Zeichenfolgen setzt voraus, dass zuerst eine minimale abgeleitete Zeichenfolge für jedes Nicht-Terminal abgerufen wird. Dies erfolgt durch weitere Modifikation des ersten Algorithmus (Lemma 4.1). Zuerst entfernen wir alle rekursiven Regeln aus dem zu verarbeitenden Regelsatz (dh mit einem LHS-Symbol in der RHS-Zeichenfolge). Es ist offensichtlich, dass keine dieser Regeln zu einer kürzeren Terminalzeichenfolge führen kann als die nicht rekursiven Regeln mit derselben LHS. Und es muss mindestens eine nicht rekursive Regel geben, wenn die LHS kein nutzloses Nicht-Terminal ist (weil nicht produktiv).
Dann gehen wir wie zuvor vor, um das Set zu erstellen Prod von produktiven Symbolen, die mit jedem Synbol assoziiert sind U eine Terminalzeichenfolge, die wir notieren σ(U). Die Saiteσ(U) wird wie bisher durch Anwendung der Regel hergestellt U→γ, Ersetzen jedes Nicht-Terminals V auftreten in γ mit σ(V). Bisher war es notwendig, dies nur auf eine Regel mit einem bestimmten Nicht-Terminal anzuwendenU
als seine LHS, die erste, die alle seine RHS-Nicht-Terminals in hätte
Prodund ignorieren Sie dann die anderen, da eine solche abgeleitete Zeichenfolge ausreichen würde. Aber wir suchen jetzt nach einer minimalen abgeleiteten Zeichenfolge. Daher für ein Nicht-TerminalUDies muss für alle Regeln mit gemacht werden Uals LHS. Wir behalten jedoch nur eine Terminalzeichenfolgeσ(U)Ersetzen des aktuellen durch den neu gefundenen, wenn der neue kleiner ist.
Außerdem, wann immer die Zeichenfolge σ(U) wird durch eine kleinere ersetzt, alle Regeln mit einem Vorkommen von Uin der RHS, die bereits verarbeitet wurde, müssen in den zu verarbeitenden Regelsatz zurückgesetzt werden, da durch Änderung die RHS auf einer kürzeren Zeichenfolge abgeleitet werden kann. Dies erfordert mehr Iterationen, endet jedoch irgendwann, da keine dieser Zeichenfolgen jemals viel kürzer als die leere Zeichenfolge wird.
Am Ende dieses ersten Algorithmus die Zeichenfolge σ(U) ist eine der kleinsten Zeichenfolgen, aus denen abgeleitet werden kann U. Es kann andere geben.
Jetzt müssen wir auch den zweiten Algorithmus modifizieren, um für jedes Nicht-Terminal zu erhalten U, (einer von) der kürzesten Zeichenfolge, die U als einziges Nicht-Terminal enthält. Zu diesem Zweck behalten wir den gleichen gerichteten Graphen mit Nicht-Terminals als Knoten bei und haben einen Bogen(U,V) Wenn es eine Regel gibt
U→αVβ. Jetzt legen wir Gewichte auf die Bögen, um die Mindestlänge des Terminalkontexts zu berechnen, die mit erreichbaren Nicht-Terminals verknüpft werden muss. Das mit dem Lichtbogen verbundene Gewicht(U,V) oben ist die Länge |σ(αβ)|, wo die Zuordnung σwird als Identität auf Terminals erweitert und dann erneut als String-Homomorphismus erweitert. Dies ist die Länge (einer) der kürzesten Terminal-Strings, die aus dem String abgeleitet werden können
αβ. Beachten Sie, dassVwird in dieser Berechnung entfernt. JEDOCH, wenn es mehrere Vorkommen von gibtVIn der RHS muss nur eine entfernt werden. Es können mehrere möglich sein(U,V) Bögen mit unterschiedlichen Gewichten, wenn es mehrere Regeln mit gibt U als LHS und Vin der RHS. In einem solchen Fall wird nur einer der leichteren Lichtbögen beibehalten.
In diesem Diagramm suchen wir nicht mehr nur nach der Erreichbarkeit von Knoten von
S, aber für den kürzesten gewichteten Pfad, der jeden Knoten vom Anfangssymbol aus erreicht S. Dies kann mit dem Dijkstra-Algorithmus erfolgen .
Gegeben der kürzeste Weg für ein Nicht-Terminal UWir lesen es wie zuvor als eine Folge von Regeln, aus denen wir eine Dérivation erhalten
S⟹∗αUβ. Dann zu jeder Regel des FormularsU→γ In der Grammatik erzeugen wir eine minimale Terminalzeichenfolge, die diese Regel als "aufruft"
σ(αγβ)
Anmerkung : Dieselbe minimale Zeichenfolge kann wahrscheinlich für mehrere Regeln verwendet werden. Aber die Tatsache, dass eine der Zeichenfolgen eine Regel verwendetρ in seiner Ableitung bedeutet dies nicht unbedingt, dass es sich um eine minimale Zeichenfolge für diese Regel handelt ρ, wie es für eine andere Regel gefunden worden sein kann, während eine kürzere für gefunden werden kann ρ. Es kann möglich sein, die Wahrscheinlichkeit zu erhöhen, dass dieselbe minimale Zeichenfolge für mehrere Regeln gefunden wird, indem eine Prioritätsrichtlinie verwendet wird, wenn Flexibilität besteht. Aber ist es die Mühe wert?
Ein schnellerer Algorithmus für minimale Terminalzeichenfolgen, die von einem Nicht-Terminal abgeleitet werden
Aufbau der Funktion σ so dass σ(U) ist eine minimale Terminalzeichenfolge, die von abgeleitet ist Uwird oben mit einer eher naiven Technik durchgeführt, die eine iterative Überprüfung der bereits geleisteten Arbeit erfordert, wenn für einige Nicht-Terminals eine neue kleinere abgeleitete Zeichenfolge gefunden wird. Dies ist verschwenderisch, auch wenn der Prozess eindeutig beendet wird.
Wir schlagen hier einen effizienteren Algorithmus vor, dh im Wesentlichen eine Anpassung an den CF-Grammatikgraphen einer Erweiterung des Dijkstra-Algorithmus für kürzeste Pfade zu und / oder Graphen mit einer korrekten Definition des Pfadkonzepts für einen und / oder Graphen . Diese Variante des Algorithmus existiert wahrscheinlich in der Literatur (vorausgesetzt, er ist korrekt), aber ich konnte ihn in den Ressourcen, auf die ich zugreifen kann, nicht finden. Daher beschreibe ich es zusammen mit einem Beweis ausführlicher.
Wie zuvor entfernen wir zuerst alle rekursiven Regeln (dh Regeln mit einem LHS-Symbol in der RHS-Zeichenfolge) aus dem zu verarbeitenden Regelsatz. Es ist offensichtlich, dass keine dieser rekursiven Regeln zu einer kürzeren Terminalzeichenfolge führen kann als die nicht rekursiven Regeln mit derselben LHS. Und für eine LHSU Es muss mindestens eine nicht rekursive vorhanden sein
U-Regel wenn das Symbol Uist kein nutzloses Nicht-Terminal (weil nicht produktiv). Dies ist nicht unbedingt erforderlich, verringert jedoch die Anzahl der Regeln, die später berücksichtigt werden müssen.
Dann gehen wir wie zuvor vor, um das Set zu erstellen Prod von produktiven Symbolen, die mit jedem Synbol assoziiert sind X eine Terminalzeichenfolge, die wir notieren σ(X)Dies ist eine Terminal-Zeichenfolge mit minimaler Größe, von der abgeleitet werden kann
X (Im vorherigen Algorithmus war dies erst nach Beendigung der Fall). Die Menge Prod wird mit allen Terminalsymbolen und für jedes Terminalsymbol initialisiert a, wir definieren σ(a)=a.
Dann betrachten wir jede Regel U→γ so dass alle RHS-Symbole in sind Prodund wir wählen eine so, dass σ(γ)ist minimal minimal. Dann fügen wir hinzuU zu Prodmit σ(U)=σ(γ)und entfernen Sie alle U-Regeln. Wir iterieren, bis alle Produktterminals eingegeben wurdenProd. Jedes Nicht-TerminalU, einmal eingegeben
Prodmuss nie wieder in Betracht gezogen werden, um sich zu ändern σ(U) für eine kleinere Saite.
Beweis :
Die vorherigen Algorithmen waren mehr oder weniger intuitiv offensichtlich. Dieser ist aufgrund des und / oder Charakters des Diagramms etwas kniffliger, und ein Beweis scheint notwendiger zu sein. Alles, was wir brauchen, ist das folgende Lemma, das die Richtigkeit des Algorithmus bei Anwendung auf die letzte Iteration feststellt.
Lemma : Nach jeder Iteration des Algorithmusσ(X) ist eine Terminal-Zeichenfolge mit minimaler Größe, von der abgeleitet werden kann X, für alle X im Prod.
Der Basisschritt ist offensichtlich, da dies per Definition für alle Terminals in gilt Prod wenn es initialisiert ist.
Angenommen, dies ist der Fall, nachdem einige Nicht-Terminals hinzugefügt wurden
Prod, Lassen U→γ als Regel ausgewählt werden, um ein neues Nicht-Terminal hinzuzufügen Prod. Wir wissen, dass diese Regel gewählt wird, weil
γ∈Prod∗ und σ(γ) ist über alle RHS aller Regeln mit einem RHS in der Größe minimal Prod∗. DannU wird hinzugefügt Prodund das müssen wir nur beweisen σ(γ) ist eine Terminal-Zeichenfolge mit minimaler Größe, von der abgeleitet werden kann U.
Dies ist offensichtlich für alle Ableitungen der Fall, die mit der Regel beginnen
U→γ, da durch Induktionshypothese, Anwendung der Kartierung σ ist so, dass alle Nicht-Terminals in σwerden durch von ihnen abgeleitete Terminal-Strings mit minimaler Größe ersetzt. Daher kann keine andere Ableitung eine kürzere Terminalzeichenfolge erzeugen.
Wir betrachten daher nur Ableitungen, die mit einer anderen beginnen U-Regel
U→β, so dass
β⟹∗w∈Σ∗, wo Σ ist der Satz von Terminalsymbolen.
Wenn β∈Prod∗, dann ist eine minimale Zeichenfolge, aus der es abgeleitet werden kann
σ(β). Aber da haben wir die Regel gewähltU→γmuss es das sein |σ(β)|≥|σ(γ)|. Also die Regel
U→β leitet sich nicht von einem kleineren terminalen Teilstring ab.
Der letzte zu berücksichtigende Fall ist, wann β∉Prod∗und wir betrachten dann eine Ableitung β⟹∗w∈Σ∗. Wenn diese Ableitung nur Nicht-Trminals in betrifftProd, dann
β∈Prod∗Dies ist ein Fall, den wir bereits gesehen haben. Daher betrachten wir nur Ableitungen, die Schritte haben, die eine Regel verwenden, deren LHS nicht in istProd. LassenV→α sei eine solche Regel, so dass
α∈Prod∗Es muss mindestens eine solche Regel geben, da sie teilweise nach Ableitungsreihenfolge geordnet sind, und w∈Prod∗.
So haben wir U⟹β⟹∗μVν. Wir wissen dasμ und ν leiten Sie auf eine Zeichenfolge mit einer Größe von mindestens 0 ab, und da nein V-Regel mit einer RHS in Prod∗ gewählt wurde, leiten sie sich an terminalen Strings ab, deren Länge mindestens gleich ist
|σ(γ)|. Daher mit der RegelU→β, U
leitet sich von einer Terminalzeichenfolge ab, deren Länge mindestens gleich ist
|σ(γ)|. ■