Hvordan konverteres finite automata til regulære udtryk?
On februar 16, 2021 by adminKonvertering af regulære udtryk til (minimal) NFA, der accepterer det samme sprog, er let med standardalgoritmer, f.eks. Thompsons algoritme . Den anden retning synes dog at være mere kedelig, og nogle gange er de resulterende udtryk rodet.
Hvilke algoritmer er der til konvertering af NFA til ækvivalente regulære udtryk? Er der fordele med hensyn til tidskompleksitet eller resultatstørrelse?
Dette formodes at være et referencespørgsmål. Medtag en generel afskrivning af din metode såvel som en ikke-trivielt eksempel.
Kommentarer
- Bemærk et lignende spørgsmål over kl. cstheory.SE , som sandsynligvis ikke passer til vores publikum.
- alle svar bruger formel teknik til at skrive RE fra DFA. Jeg mener, at min teknik ved analyse er forholdsvis let og objektiv, som jeg demonstrerer i mit svar : Hvad er sproget i denne deterministiske endelige automat? Jeg føler, det vil være nyttigt engang. Ja, selvfølgelig bruger jeg selvfølgelig formel metode (Arden sætning) at skrive RE er spørgsmål er komplekst som givet i dette eksempel: Sådan skriver du regulært udtryk for en DFA
Svar
Der er flere metoder til at udføre konverteringen fra endelig automata til regulære udtryk. Her vil jeg beskrive den, der normalt undervises i skolen, som er meget visuel. Jeg tror, det er det mest anvendte i praksis. At skrive algoritmen er dog ikke sådan en god ide.
Tilstandsfjerningsmetode
Denne algoritme handler om at håndtere automatens graf og er derfor ikke særlig velegnet til algoritmer, da den har brug for graf primitiver såsom … tilstand fjernelse. Jeg vil beskrive det ved hjælp af primitiver på højere niveau.
Nøgleidéen
Ideen er at overveje regelmæssige udtryk på kanter og derefter fjerne mellemliggende tilstande, mens kantetiketterne holdes konsistente.
Hovedmønsteret kan ses i det følgende til figurer. Den første har etiketter mellem $ p, q, r $, der er regulære udtryk $ e, f, g, h, i $, og vi vil fjerne $ q $.
Når vi først er fjernet, komponerer vi $ e, f, g, h, i $ sammen (mens vi bevarer de andre kanter mellem $ p $ og $ r $, men dette vises ikke på dette):
Eksempel
Brug det samme eksempel som i Raphaels svar :
vi fjerner successivt $ q_2 $:
og derefter $ q_3 $:
så skal vi stadig anvende en stjerne på udtrykket fra $ q_1 $ til $ q_1 $. I dette tilfælde er den endelige tilstand også initial, så vi skal bare tilføje en stjerne:
$$ (ab + (b + aa) (ba) ^ * (a + bb)) ^ * $$
Algoritme
L[i,j]
er regexp af sproget fra $ q_i $ til $ q_j $. Først fjerner vi alle mul ti-kanter:
for i = 1 to n: for j = 1 to n: if i == j then: L[i,j] := ε else: L[i,j] := ∅ for a in Σ: if trans(i, a, j): L[i,j] := L[i,j] + a
Nu fjernes tilstanden. Antag, at vi vil fjerne staten $ q_k $:
remove(k): for i = 1 to n: for j = 1 to n: L[i,i] += L[i,k] . star(L[k,k]) . L[k,i] L[j,j] += L[j,k] . star(L[k,k]) . L[k,j] L[i,j] += L[i,k] . star(L[k,k]) . L[k,j] L[j,i] += L[j,k] . star(L[k,k]) . L[k,i]
Bemærk at både med en blyant papir og med en algoritme skal du forenkle udtryk som star(ε)=ε
, e.ε=e
, ∅+e=e
, ∅.e=∅
hånd skal du bare ikke skrive kanten, når det ikke er $ ∅ $ eller endda $ ε $ for en selvsløjfe, og du ignorerer, når der ikke er nogen overgang mellem $ q_i $ og $ q_k $ eller $ q_j $ og $ q_k $)
Hvordan skal jeg nu bruge remove(k)
? Du bør ikke fjerne de endelige eller indledende tilstande let, ellers går du glip af dele af sproget.
for i = 1 to n: if not(final(i)) and not(initial(i)): remove(i)
Hvis du kun har en endelig tilstand $ q_f $ og en starttilstand $ q_s $, så er det endelige udtryk:
e := star(L[s,s]) . L[s,f] . star(L[f,s] . star(L[s,s]) . L[s,f] + L[f,f])
Hvis du har flere endelige tilstande (eller endda indledende tilstande), er der ingen enkel måde at flette sammen disse, bortset fra at anvende den transitive lukningsmetode. Normalt er dette ikke et problem i hånden, men det er akavet, når du skriver algoritmen. En meget enklere løsning er at opregne alle par $ (s, f) $ og køre algoritmen på (allerede fjernet tilstands) graf for at få alle udtryk $ e_ {s, f} $ antager $ s $ er den eneste indledende tilstand og $ f $ er den eneste endelige tilstand, så gør foreningen af alle $ e_ {s, f} $.
Dette og det faktum, at dette ændrer sprog mere dynamisk end den første metode gør det mere fejlbehæftet ved programmering. Jeg foreslår at bruge en hvilken som helst anden metode.
Ulemper
Der er mange tilfælde i denne algoritme, for eksempel for at vælge hvilken node vi skal fjerne, antallet af endelige tilstande i slutningen , det faktum, at en endelig tilstand også kan være initial osv.
Bemærk at nu, hvor algoritmen er skrevet, ligner det meget den transitive lukningsmetode.Kun sammenhængen med brugen er anderledes. Jeg anbefaler ikke at implementere algoritmen, men det er en god idé at bruge metoden til at gøre det i hånden.
Kommentarer
- I eksemplet, 2. billede efter fjernelse af node ” 2 “, der mangler en kant – loop-kant (ab) i node A.
- @Kabamaru: Fixed. Men nu synes jeg $ \ varepsilon $ i 3. billede også skal være
ab
, og lignende måske i det endelige regulære udtryk. - Du kan lave algoritmen arbejde for et vilkårligt antal indledende og endelige tilstande ved at tilføje en ny indledende $ q ^ + $ og en ny endelig tilstand $ q ^ – $, og forbinde disse til de oprindelige indledende og endelige tilstande med $ \ varepsilon $ -kanter. Fjern nu alle de oprindelige tilstande. Udtrykket findes derefter ved den eneste resterende kant fra $ q ^ + $ til $ q _- $. Konstruktionen giver ikke sløjfer på $ q ^ + $ eller $ q _- $, da disse stater ikke har indgående resp. udgående kanter. Eller hvis du er streng, har de etiketter, der repræsenterer det tomme sæt.
- Der er stadig et problem med det andet eksempel: før forenkling accepterer automaten ” ba “, (1, 3, 1) men efter forenkling ‘ t.
Svar
Metode
Den bedste metode, jeg har set, er en, der udtrykker automaten som ligningssystem for (almindelige) sprog, der kan blive løst. Det er især pænt, da det ser ud til at give mere kortfattede udtryk end andre metoder.
Lad $ A = (Q, \ Sigma, \ delta, q_0, F) $ en NFA uden $ \ varepsilon $ – overgange. For hver stat $ q_i $ skal du oprette ligningen
$ \ qquad \ displaystyle Q_i = \ bigcup \ limits_ {q_i \ overset {a} {\ to} q_j} aQ_j \ cup \ begin {cases} \ {\ varepsilon \} &, \ q_i \ i F \\ \ emptyset &, \ text {else} \ end {cases} $
hvor $ F $ er sættet med endelige tilstande og $ q_i \ overskud {a} {\ til} q_j $ betyder, at der er en overgang fra $ q_i $ til $ q_j $ mærket med $ a $ . Hvis du læser $ \ cup $ som $ + $ eller $ \ mid $ (afhængigt af din definition af dit regulære udtryk), kan du se, at dette er en ligning af regulære udtryk.
For at løse systemet har du brug for associativitet og distribution af $ \ cup $ og $ \ cdot $ (streng sammenkædning), kommutativitet af $ \ cup $ og Ardens Lemma ¹:
Lad $ L, U, V \ subseteq \ Sigma ^ * $ almindelige sprog med $ \ varepsilon \ notin U $. Derefter
$ \ qquad \ displaystyle L = UL \ cup V \ quad \ Longleftrightarrow \ quad L = U ^ * V $
Løsningen er et sæt regulære udtryk $ Q_i $, en for hver stat $ q_i $. $ Q_i $ beskriver nøjagtigt de ord, der kan accepteres af $ A $, når de startes i $ q_i $; derfor er $ Q_0 $ (hvis $ q_0 $ er den oprindelige tilstand) ønsket udtryk.
Eksempel
Af hensyn til klarheden betegner vi singletonsæt efter deres element, dvs. $ a = \ {a \} $. eksempel skyldes Georg Zetzsche.
Overvej t hans NFA:
[ kilde ]
Det tilsvarende ligningssystem er:
$ \ qquad \ begin {align} Q_0 & = aQ_1 \ cup bQ_2 \ cup \ varepsilon \\ Q_1 & = bQ_0 \ cup aQ_2 \\ Q_2 & = aQ_0 \ cup bQ_1 \ end {align} $
Sæt nu den tredje ligning i den anden:
$ \ qquad \ begin {align} Q_1 & = bQ_0 \ cup a (aQ_0 \ cup bQ_1) \\ & = abQ_1 \ cup (b \ cup aa) Q_0 \\ & = (ab) ^ * ( b \ cup aa) Q_0 \ end {align} $
For det sidste trin anvender vi Ardens Lemma med $ L = Q_1 $, $ U = ab $ og $ V = (b \ cup aa) \ cdot Q_0 $. Bemærk, at alle tre sprog er regelmæssige og $ \ varepsilon \ notin U = \ {ab \} $, hvilket gør det muligt for os at anvende lemmaet. Nu tilslutter vi dette resultat til den første ligning:
$ \ qquad \ begin {align} Q_0 & = a (ab) ^ * (b \ cup aa ) Q_0 \ cup baQ_0 \ cup bb (ab) ^ * (b \ cup aa) Q_0 \ cup \ varepsilon \\ & = ((a \ cup bb) (ab) ^ * (b \ cup aa) \ cup ba) Q_0 \ cup \ varepsilon \\ & = ((a \ cup bb) (ab) ^ * (b \ cup aa) \ cup ba) ^ * \ qquad \ text {(af Ardens Lemma)} end {align} $
Således har vi fundet et regelmæssigt udtryk for det sprog, der accepteres af ovenstående automat, nemlig
$ \ qquad \ displaystyle ((a + bb) (ab) ^ * (b + aa) + ba) ^ *. $
Bemærk at det er ret kortfattet (sammenlign med resultat af andre metoder) men ikke entydigt bestemt; løsning af ligningssystemet med en anden sekvens af manipulationer fører til andre – ækvivalente! – udtryk.
- For et bevis på Arden ” s Lemma, se her .
Kommentarer
- Hvad er tidskompleksiteten af denne algoritme? Er der en grænse for størrelsen på det producerede udtryk?
- @jmite: Jeg aner ikke. Jeg tror ikke ‘ jeg ‘ forsøger at implementere dette (andre metoder synes at være mere gennemførlige i denne henseende) men brug det som en pen-og-papir-metode.
- Her ‘ sa Prolog implementering af denne algoritme: github.com / wvxvw / intro-to-automata-theory / blob / master / automata / … men dens
maybe_union/2
predikat kunne bruge mere arbejde (specielt wrt eliminerer fælles præfiks) for at gøre pænere regelmæssige udtryk. En anden måde at se denne metode på er at forstå den som oversættelse fra regex til højre-lineær grammatik, hvor sprog med Prolog-lignende forening eller ML-lignende mønstermatchning giver en meget god transducer, så den ‘ er ikke kun en pen-og-papir-algoritme 🙂 - Bare et spørgsmål. Ε i den første ligning er, fordi Qo er en starttilstand, eller fordi den ‘ er en endelig tilstand? Samme måde, hvis jeg har to sluttilstande, gælder?
- @PAOK Kontroller definitionen af $ Q_i $ ovenfor (linjen); det ‘ s fordi $ q_0 $ er en endelig tilstand.
Svar
Brzozowski algebraisk metode
Dette er den samme metode som beskrevet i Raphaels svar , men fra et punkt af visning af en systematisk algoritme og derefter algoritmen. Det viser sig at være let og naturligt at implementere, når du først ved, hvor du skal begynde. Det kan også være lettere for hånd, hvis det ikke er praktisk at tegne alle automaterne af en eller anden grund. / p>
Når du skriver en algoritme, skal du huske, at ligningerne altid skal være lineære, så du har en god abstrakt gengivelse af ligningerne, noget du kan glemme, når du løser i hånden.
Idéen med algoritmen
Jeg vil ikke beskrive, hvordan det fungerer, da det er godt udført i Raphaels svar , som jeg foreslår at læse før. I stedet fokuserer jeg på i hvilken rækkefølge du skal løse ligningerne uden at gøre for mange ekstra co mputationer eller ekstra tilfælde.
Startende fra Ardens regel “s geniale løsning $ X = A ^ * B $ til sprogligningen $ X = AX∪B $ kan vi betragte automaten som et sæt ligninger af formen:
$$ X_i = B_i + A_ {i, 1} X_1 +… + A_ {i, n} X_n $$
kan vi løse dette ved induktion på $ n $ ved at opdatere arrays $ A_ {i, j} $ og $ B_ {i, j} $ i overensstemmelse hermed. På trin $ n $ har vi:
$$ X_n = B_n + A_ {n, 1} X_1 +… + A_ {n, n} X_n $$
og Ardens regel giver os:
$$ X_n = A_ {n, n} ^ * (B_n + A_ {n, 1} X_1 + … + A_ {n, n-1} X_ {n -1}) $$
og ved at indstille $ B “_n = A_ {n, n} ^ * B_n $ og $ A” _ {n, i} = A_ {n, n} ^ * A_ {n, i} $ får vi:
$$ X_n = B “_n + A” _ {n, 1} X_1 +… + A “_ {n, n-1} X_ {n -1} $$
og vi kan derefter fjerne alle behov for $ X_n $ i systemet ved at indstille for $ i, j < n $:
$$ B “_i = B_i + A_ {i, n} B” _n $$ $$ A “_ {i, j} = A_ {i, j} + A_ {i, n} A “_ {n, j} $$
Når vi har løst $ X_n $ når $ n = 1 $, får vi en ligning som denne:
$$ X_1 = B” _1 $$
uden $ A “_ {1, i} $. Således fik vi vores regulære udtryk.
Algoritmen
Takket være dette kan vi bygge algoritmen. For at have den samme konvention end i induktionen ovenfor, vil vi sige, at den oprindelige tilstand er $ q_1 $, og at antallet af stater er $ m $. Først initialiseringen for at udfylde $ B $:
for i = 1 to m: if final(i): B[i] := ε else: B[i] := ∅
og $ A $:
for i = 1 to m: for j = 1 to m: for a in Σ: if trans(i, a, j): A[i,j] := a else: A[i,j] := ∅
og derefter løsningen:
for n = m decreasing to 1: B[n] := star(A[n,n]) . B[n] for j = 1 to n: A[n,j] := star(A[n,n]) . A[n,j]; for i = 1 to n: B[i] += A[i,n] . B[n] for j = 1 to n: A[i,j] += A[i,n] . A[n,j]
det sidste udtryk er så:
e := B[1]
Implementering
Selvom det kan synes at være et ligningssystem, der synes for symbolsk for en algoritme, er denne velegnet til en implementering. Her er en implementering af denne algoritme i Ocaml (brudt link) . Bemærk at bortset fra funktionen brzozowski
, er alt at udskrive eller bruge til Raphaels eksempel. Bemærk at der er en overraskende effektiv funktion til forenkling af regulære udtryk simple_re
.
Kommentarer
- Linket er død …
- Implementering i Javascript: github.com/devongovett/regexgen/blob/master/src/regex.js
- Tak for denne fantastiske forklaring. Hvis jeg forstår korrekt, din initialisering pseudokode antager, at der for givet i og j højst er en sådan, at (i, a, j) er en overgang. Dette er korrekt, hvis vi er enige om at mærke denne overgang med den regexp, der matcher alle bogstaverne i Σ at etiketten overgår fra i til j i bogstavautomaten, men så er din notation a in a lidt underlig, da det ikke rigtig er et bogstav. Hvis vi går bogstav for bogstav, kan vi have flere overgange fra i til j og skal gøre sammenkoblingen af deres etiketter i sløjfekroppen.
Svar
Transitiv lukningsmetode
Denne metode er let at skrive i en form af en algoritme, men genererer absurd store regulære udtryk og er upraktisk, hvis du gør det i hånden, hovedsagelig fordi dette er for systematisk. Det er dog en god og enkel løsning til en algoritme.
Nøgleidéen
Lad $ R ^ k_ {i, j} $ repræsentere det regulære udtryk for strengene, der går fra $ q_i $ til $ q_j $ ved hjælp af staterne $ \ {q_1,…, q_k \} $. Lad $ n $ være antallet af tilstande for automaten.
Antag at du allerede kender det regulære udtryk $ R_ {i, j} $ fra $ q_i $ til $ q_j $ uden den mellemliggende tilstand $ q_k $ (undtagen ekstremiteter), for alle $ i, j $. Derefter kan du gætte, hvordan tilføjelse af en anden tilstand vil påvirke det nye regulære udtryk $ R “_ {i, j} $: det ændres kun, hvis du har direkte overgange til $ q_k $, og det kan udtrykkes således:
$$ R “_ {i, j} = R_ {i, j} + R_ {i, k}. R_ {k, k} ^ *. R_ {k, j} $$
($ R $ er $ R ^ {k-1} $ og $ R “$ er $ R ^ k $.)
Eksempel
Vi bruger det samme eksempel som i Raphaels svar . Først kan du kun bruge de direkte overgange.
Her er det første trin (bemærk, at en selvsløjfe med en etiket $ a $ ville have forvandlet den første $ ε $ til $ (ε + a) $.
$$ R ^ 0 = \ begin {bmatrix} ε & a & b \\ b & ε & a \\ a & b & ε \ end {bmatrix} $$
På det andet trin kan vi bruge $ q_0 $ (som omdøbes til $ q_1 $ for os, fordi $ R ^ 0 $ allerede bruges til formålet ovenfor). Vi vil se, hvordan $ R ^ 1 $ fungerer.
Fra $ q_2 $ til $ q_2 $: $ R ^ 1_ {2,2} = R ^ 0_ {2,2} + R ^ 0_ {2,1} {R ^ 0_ {1,1}} ^ * R ^ 0_ {1,2} = ε + b ε ^ * a = ε + ba $.
Hvorfor er det? Det er fordi at gå fra $ q_2 $ til $ q_2 $ ved kun at bruge $ q_1 $ som en mellemstatus kan gøres ved at blive her ($ ε $) eller gå til $ q_1 $ ($ a $) og løbe der ($ ε ^ * $) og kommer tilbage ($ b $).
$$ R ^ 1 = \ begin {bmatrix} ε & a & b \\ b & ε + ba & a + bb \\ a & b + aa & ε + ab \ end {bmatrix} $$
Du kan også beregne $ R ^ 2 $ og $ R ^ 3 $ og $ R ^ 3_ {1,1 } $ giver dig det endelige udtryk, fordi $ 1 $ er både indledende og endelig. Bemærk, at der er gjort meget forenkling af udtryk her. Ellers ville den første $ a $ på $ R ^ 0 $ være $ (∅ + a) $ og den første $ a $ på $ R ^ 1 $ ville være $ ((∅ + a) + ε (ε) ^ * a ) $.
Algoritme
Initialisering:
for i = 1 to n: for j = 1 to n: if i == j: R[i,j,0] := ε else: R[i,j,0] := ∅ for a in Σ: if trans(i, a, j): R[i,j,0] := R[i,j,0] + a
Transitiv lukning:
for k = 1 to n: for i = 1 to n: for j = 1 to n: R[i,j,k] := R[i,j,k-1] + R[i,k,k-1] . star(R[k,k,k-1]) . R(k,j,k-1)
Så er det endelige udtryk (hvis $ q_s $ er den oprindelige tilstand):
e := ∅ for i = 1 to n: if final(i): e := e + R[s,i,n]
Men du kan forestille dig det genererer grimme regulære udtryk. Du kan virkelig forvente ting som $ (∅) ^ * + (a + (∅) ^ *) (ε) ^ * (a + ∅) $, der repræsenterer det samme sprog som $ aa $. Bemærk, at forenkling af et regulært udtryk er nyttigt i praksis.
Skriv et svar