Języki, automaty i obliczenia/Wykład 5: Algorytmy konstrukcji automatu minimalnego

Z Studia Informatyczne
Wersja z dnia 18:43, 22 sie 2006 autorstwa Arek (dyskusja | edycje)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)
Przejdź do nawigacjiPrzejdź do wyszukiwania

{ Algorytmy konstrukcji automatu minimalnego }

Wprowadzenie
W tym wykładzie podamy algorytmy konstrukcji automatu minimalnego

i twierdzenia dowodzące ich poprawności.

Słowa kluczowe
automat minimalny, pochodna Brzozowskiego, algorytmy

minimalizacji.

algorytmy konstrukcji automatu minimalnego

Dla języka rozpoznawanego L konstrukcję automatu minimalnego można rozpocząć, startując z opisu języka danego na przykład przez wyrażenie regularne lub też jakiegoś automatu rozpoznającego ten język. W niniejszym wykładzie przedstawimy algorytmy konstrukcji automatu minimalnego obejmujące oba wspomniane punkty startu. Jako pierwszy, nawiązując do rezulatów przedstawionych w poprzednim wykładzie, prezentujemy algorytm, dla którego punktem wyjścia jest język L. Prezentację poprzedzimy wprowadzeniem pewnej operacji na słowach zwanej pochodną J.Brzozowskiego.

Definicja

Niech LA* będzie dowolnym językiem, a uA* dowolnym słowem. Pochodną Brzozowskiego (residuum) z języka L względem słowa u nazywamy język

u1L={wA*:uwL}.

Podczas obliczeń pochodnych Brzozowskiego (residuów języka L) można wykorzystać poniższe równości.

Niech L1,L2A* będą dowolnymi językami, aA dowolną literą, a u,vA* dowolnymi słowami. Prawdziwe są następujące równości:

Parser nie mógł rozpoznać (nieznana funkcja „\aligned”): {\displaystyle \displaystyle \aligned \nonumber u^{-1}(L_1 \cup L_2) &= u^{-1}L_1 \cup u^{-1}L_2, \\ \nonumber u^{-1}(L_1 \backslash L_2) &= u^{-1}L_1 \backslash u^{-1}L_2, \\ \nonumber u^{-1}(L_1 \cap L_2) &= u^{-1}L_1 \cap u^{-1}L_2, \\ \nonumber a^{-1}(L_1L_2) &= (a^{-1}L_1)L_2 } , jeśli Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle 1 \notin L_1, \\ \nonumber a^{-1}(L_1L_2) &= (a^{-1}L_1)L_2 \cup a^{-1}L_2 } , jeśli Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle 1 \in L_1, \\ \nonumber a^{-1}L^* &= (a^{-1}L)L^*, \\ \nonumber v^{-1}(u^{-1}L) &= (uv)^{-1}L. \endaligned}

Przykład

Obliczmy wszystkie pochodne dla języka L=a+b+. Okazuje się, że są tylko cztery różne pochodne liczone względem a, b, ab i słowa pustego 1. Mianowicie:
a1L=a*b+,
b1L=,
ab1L=b*,
11L=L.
Dla wszystkich innych słów otrzymujemy uzyskane powyżej języki, co wynika z własności pochodnych (patrz wyżej wypisane równości) i z następujacych obliczeń:
n(an)1L=a*b+,
n(bn)1L=,
n(abn)1L=b*.

Zauważmy również, nawiązując raz jeszcze do rezulatów przedstawionych w poprzednim wykładzie, że prawdziwa jest następująca równoważność wiążąca wprowadzone pojęcie pochodnej Brzozowskiego z prawą kongruencją syntaktyczną:

uPrLvu1L=v1L.

Rozpisując z definicji lewą stronę tej równoważności, otrzymujemy, iż dla dowolnego słowa zA* słowo uzL wtedy i tylko wtedy, gdy vzL. A to równoważnie oznacza (znów z definicji), że u1L=v1L.

Z uzasadnionej równoważności oraz twierdzenia 3.4 o prawej kongruencji syntaktycznej z poprzedniego wykładu wnioskujemy równoważność rozpoznawalności języka L i skończonej ilości różnych pochodnych Brzozowskiego tego języka.

Pierwszy z przedstawianych algorytmów będzie konstruował automat minimalny, wyznaczając prawą kongruencję automatową poprzez zastosowanie metody pochodnych Brzozowskiego. Metoda ta umożliwia przeprowadzanie odpowiednich obliczeń bezpośrednio na wyrażeniach regularnych. Ogólny opis algorytmu jest następujący. Stany konstruowanego automatu minimalnego etykietowane są zbiorami odpowiadającymi pewnym językom. Mając dany język L, ustanawiamy stan początkowy automatu jako L, wpisujemy go na listę i obliczamy a1L dla każdej litery aA. Jeśli wśród obliczonych wartości znajduje się język niewystępujący na liście, dodajemy go do listy. Obliczenia pochodnych Brzozowskiego wykonujemy, dopóki będziemy uzyskiwać nowe języki (nowe stany). Funkcja przejść konstruowanego automatu minimalnego zdefiniowana jest następująco:

f(X,a)=a1X,

gdzie X jest pewnym językiem z listy . Obliczone języki określają stany automatu minimalnego.


dla dociekliwych - start ----

Obliczone języki określające stany automatu minimalnego to elementy monoidu syntaktycznego języka L.


dla dociekliwych - end ----

Automatem minimalnym dla automatu 𝒜=(S,A,f,s0,T) będzie zatem automat

𝒜L=(SL,A,fL,s0L,TL),

gdzie:

  • SL={u1L: uA*},
  • s0L=L,
  • TL={u1L: uL},
  • fL(u1L,a)=a1(u1L)=(ua)1L.

Jeśli zdefiniujmy odwzorowanie ν:SSL, kładąc:

ν(s)=u1L, gdzie s=f(s0,u),

to można dowieść, że ν jest dobrze określone, jest epimorfizmem oraz ν(s0)=s0L - porównaj twierdzenie 3.1 z wykładu 4. Prawdą jest też, iż sT wtedy i tylko wtedy, gdy ν(s)TL oraz że następujący diagram komutuje:

Parser nie mógł rozpoznać (nieznana funkcja „\beginCD”): {\displaystyle \displaystyle \beginCD s @>{a}>> s' @. \ \ \ \ } w Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle \mathcal{A} \\ @V{\nu}VV @V{\nu}VV \\ u^{-1}L @>{a}>> (ua)^{-1}L @. \ \ \ \ } w Parser nie mógł rozpoznać (nieznana funkcja „\endCD”): {\displaystyle \displaystyle \mathcal{A}_L. \endCD }

Formalny zapis algorytmu przedstawiony jest poniżej.

Algorytm


{Minimalizuj1 - algorytm minimalizacji wykorzystujący pochodne Brzozowskiego}

[1] Wejście: 𝒜=(S,A,f,s0,T) - automat taki, że L=L(𝒜)

Wyjście: automat minimalny 𝒜=(SL,A,fL,sL,TL) dla 𝒜SL{L};

włóż(,L);

while =M zdejmij();

for each aANa1M;

if NSL=SLSL{N};

włóż(,N);

endif

endfor

endwhile

for each MSL

for each aAfL(M,a)a1M;

endfor

endfor

sLL;

TL{u1L: uL};

return 𝒜;


Funkcja zdejmij(), występująca w linii 6., zdejmuje z kolejki pierwszy element i zwraca go jako swoją wartość. Procedura włóż(,L), występująca w liniach 4. oraz 11., wstawia na koniec kolejki element L.

Przykład

Dla języka L=a+b+ z przykładu 1.1 w wyniku działania powyższego algorytmu otrzymamy czterostanowy automat 𝒜L=(SL,A,f,L,T), gdzie
SL={L,a*b+,,b*},
a funkcja przejść zadana jest grafem:

RYSUNEK ja-lekcja5-w-rys1

Prezentowane poniżej twierdzenie uzasadnia kolejny algorytm konstrukcji automatu minimalnego. Tym razem punktem wyjścia jest dowolny automat rozpoznający język L .


dla dociekliwych - start -----

Algorytm oblicza również monoid syntaktyczny języka L .


dla dociekliwych - end -----

Analogicznie do konstrukcji relacji ρi , przedstawionej w wykładzie 4, możemy określić ciąg odpowiednich relacji na zbiorze stanów dowolnego automatu rozpoznającego język L . Relacje te służą do efektywnego określenia automatu minimalnego, równoważnego zadanemu.

Twierdzenie

Niech 𝒜=(S,A,f,s0,T) będzie dowolnym automatem i niech L=L(𝒜) . Przez Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle \: \approx _{\mathcal{A}} \displaystyle \subset S \times S \; } oznaczmy relację równoważności zawierającą dwie klasy równoważności T i ST. Przez ρi dla i oznaczmy zstępujący ciąg relacji określony następująco:

ρ1=𝒜 , a dla i=2,... przyjmijmy

ρi={(s,t)S×S:aA{1}f(s,a)ρi1f(t,a)}.

{ Wtedy ρi jest największą prawą kongruencją automatową zawartą w relacji ρ1=𝒜 i automat minimalny ma postać}

𝒜L=(S/ρi,A,f*,[s0],T/ρi).

Dowód tego twierdzenia przebiega analogicznie do dowodu twierdzenia 3.3 z wykładu 4.

Algorytm działajacy w oparciu o powyższe twierdzenie na podstawie zadanego automatu 𝒜=(S,A,f,s0,T), konstruuje efektywnie automat minimalny dla 𝒜, obliczając ciąg relacji ρi. Proces konstrukcji przebiega w sposób iteracyjny. Zaczynamy od zdefiniowania relacji 𝒜, która posiada tylko dwie klasy abstrakcji: do pierwszej z nich należą wszystkie stany końcowe, a do drugiej -- wszystkie pozostałe stany. Tak więc

s,tS s𝒜t(sTtT)(sSTtST).

Definiujemy pierwszą z relacji ρ, czyli relację ρ1 jako równą 𝒜, a następnie, dla każdej obliczonej już relacji ρi1, obliczamy relację ρi w następujący sposób:

s1ρis2(s1ρi1s2)(aA f(s1,a)ρi1f(s2,a)).

Nowo obliczona relacja ρi albo podzieli jedną lub kilka klas abstrakcji relacji ρi1, albo będzie identyczna z relacją ρi1. Jeśli zajdzie ta druga sytuacja, to znaczy, że dla każdego j>i w oczywisty sposób spełniona jest równość ρj=ρi, czyli ciąg relacji ustabilizuje się. W tym momencie algorytm kończy swoje działanie i klasy abstrakcji relacji ρi będą reprezentować stany automatu minimalnego.

Algorytm


{Minimalizuj2 -- algorytm minimalizacji automatu wykorzystujący stabilizujący się ciąg relacji}

[1] Wejście: 𝒜=(S,A,f,s0,T) -- automat taki, że L=L(𝒜).

Wyjście: automat minimalny 𝒜=(S,A,f,s0,T) dla 𝒜.

ρ1𝒜;

i1;

Parser nie mógł rozpoznać (nieznana funkcja „\slash”): {\displaystyle \displaystyle \slash \slash} oblicz ρi: s1ρis2(s1ρi1s2)(aA f(s1,a)ρi1f(s2,a));

ii+1;

empty(ρi)

for each (s1,s2)S×S

flagtrue;

for each aA

if not f(s1,a)ρi1f(s2,a)

flagfalse;

endif

endfor

if flag=true and s1ρi1s2ρiρi{(s1,s2)};

endif

endfor

{ρi=ρi1}

Parser nie mógł rozpoznać (nieznana funkcja „\slash”): {\displaystyle \displaystyle S' \leftarrow S \slash \overline{\rho}_i} ;

for each Parser nie mógł rozpoznać (nieznana funkcja „\slash”): {\displaystyle \displaystyle [s]_{\overline{\rho}_i} \in S \slash \overline{\rho}_i}

for each aAf([s]ρi,a)[f(s,a)]ρi;

endfor

endfor

s0[s0]ρi;

T{[t]ρi: tT};

return 𝒜=(S,A,f,s0,T);


Przykład

Zminimalizujemy automat 𝒜=(S,A,f,s0,T), dla którego
S={s0,s1,s2,s3,s4,s5},A={a,b},T={s1,s2,s4} , a funkcja przejść określona jest przy pomocy grafu.
RYSUNEK ja-lekcja5-w-rys2
Konstruujemy ciąg relacji ρi.

Na początku ρ1 dzieli S na dwie klasy abstrakcji; pierwsza zawiera stany końcowe, a druga -- wszystkie pozostałe, czyli uzyskujemy dwa zbiory {s1,s2,s4} oraz {s0,s3,s5}.

Obliczmy ρ2 (pierwszy przebieg pętli w liniach 5.-20. algorytmu). Aby dwa elementy (stany) s i t były ze sobą w relacji ρ2 muszą być ze sobą w relacji ρ1 oraz musi zachodzić

aAf(s,a)ρ1f(t,a).

Czyli kolejna, nowa relacja może ewentualnie podzielić już istniejące zbiory zdefiniowane przez poprzednią relację. Nie może więc zajść taka sytuacja, że w jednej klasie abstrakcji relacji ρi+1 znajdą się elementy z różnych klas abstrakcji relacji ρi.

Rozważmy najpierw zbiór {s1,s2,s4}. Oczywiście każde dwa stany z tego zbioru są ze sobą w relacji ρ1. Zauważmy, że f(s2,a)=f(s4,a)=s2, f(s2,b)=f(s4,b)=s4, więc s2ρ2s4 (bo s2ρ1s2 oraz s4ρ1s4). Ponieważ f(s1,b)=s5 i f(s2,b)=s4, a wiemy, że s5 nie jest w relacji ρ1 z s4, zatem stany s1 i s2 nie mogą być ze sobą w relacji ρ2, a to oznacza, że także stany s1 i s4 nie mogą być ze sobą w relacji ρ2.

W analogiczny sposób można sprawdzić, że relacja ρ2 nie podzieli zbioru {s0,s3,s5}. Ostatecznie, po pierwszym wykonaniu pętli algorytmu minimalizacji obliczyliśmy relację ρ2, która dzieli S na następujące podzbiory:

{s1},{s2,s4},{s0,s3,s5}.

W kolejnym kroku obliczamy ρ3. Zbiór {s1} oczywiście nie może być już podzielony na mniejsze podzbiory. Łatwo zauważyć, że ρ3 nie podzieli także zbioru {s2,s4}.

Rozważmy teraz zbiór {s0,s3,s5}. Mamy f(s3,a)=f(s5,a) oraz f(s3,b)=s3, f(s5,b)=s5 i wiadomo, że s3ρ2s5, zatem s3 i s5 będą ze sobą w relacji ρ3.

Ponieważ f(s0,a)=s2 i f(s3,a)=s1, ale s2 i s1 nie są ze sobą w relacji ρ2, zatem nie mogą być także ze sobą w relacji ρ3. Relacja ρ3 dzieli więc zbiór {s0,s3,s5} na zbiory {s0} oraz {s3,s5}.

Podział zbioru S przez relację ρ3 wygląda więc

następująco:
{s0},{s1},{s2,s4},{s3,s5}.

Relacja ρ4 nie podzieli już ani zbioru {s2,s4}, ani zbioru {s3,s5}, więc uzyskujemy równość ρ4=ρ3 i ponieważ ciąg relacji się ustabilizował, algorytm kończy działanie.

Podsumowując, mamy:

ρ1
{s1,s2,s4},{s0,s3,s5},
ρ2
{s1},{s2,s4},{s0,s3,s5},
ρ3
{s1},{s2,s4},{s0},{s3,s5}.ρ3=ρ4 i równoważny minimalny automat 𝒜L=(S,f*,s0,T) ma 4 stany.

q0={s0},q1={s1},q2={s2,s4},q3={s3,s5}, T={q1,q2}. Jak łatwo zauważyć jest to automat z przykładu 3.1 zamieszczonego w wykładzie 4.

RYSUNEK ja-lekcja5-w-rys3

Jednym z najczęściej stosowanych algorytmów automatu minimalnego jest algorytm, który buduje "tabelkę" na podstawie której określa się automat minimalny. Poprawność tego algorytmu również uzasadnia twierdzenie Uzupelnic twrho|.

W algorytmie tym wyznaczać będziemy tzw. stany rozróżnialne. Algorytm działa w czasie O(|A|n2), gdzie |A| jest mocą alfabetu, a n -- liczbą stanów automatu wejściowego, czyli podlegajacego minimalizacji. Złożoność pamięciowa jest również O(|A|n2). Prezentowany algorytm nosi nazwę algorytmu Hopcrofta-Ullmana. Znana w literaturze jest pewna zmodyfikowana wersja tego algorytmu. Jest to algorytm Aho-Sethiego-Ullmana, który ma tę samą złożoność czasową, ale lepszą złożoność pamięciową - O(|A|n). Natomiast w ramach ćwiczeń prezentujemy jeszcze jeden algorytm, znany jako algorytm minimalizacji Hopcrofta. Czas działania tego algorytmu wynosi O(nlogn).

Niech będzie relacją zdefiniowaną przez funkcję przejść automatu w następujący sposób:

pqwA* (f(p,w)Tf(q,w)T).

Definicja

Stany p i q są równoważne, jeśli pq.

Jeśli stany nie są równoważne, to będziemy mówić, że są rozróżnialne.

Zadaniem algorytmu jest wyznaczenie stanów równoważnych, celem ich utożsamienia ze sobą. Algorytm musi zdecydować, dla każdej pary stanów (p,q), czy są one rozróżnialne. Jeśli pod koniec działania algorytmu okaże się, że nie stwierdziliśmy rozróżnialności tych stanów, to znaczy, że są one równoważne; następuje ich utożsamienie, czyli "połączenie" ich w jeden stan. Gdy takiego połączenia dokonamy dla wszystkich par stanów, wobec których nie stwierdziliśmy ich rozróżnialności, powstanie automat o minimalnej liczbie stanów.

W praktyce algorytm nie wyznacza stanów równoważnych, ale stany rozróżnialne, gdyż jest to po prostu łatwiejsze. Po wyznaczeniu wszystkich par stanów rozróżnialnych pozostałe pary stanowić będą stany równoważne.

W algorytmie wykorzystywać będziemy tablicę list [p,q], po jednej liście dla każdej pary stanów. Funkcja initialize([p,q]) inicjalizuje listę pustą, funkcja zdejmij([p,q]) zdejmuje jeden z elementów, natomiast funkcja Parser nie mógł rozpoznać (błąd składni): {\displaystyle \displaystyle \textbf{włóż}(\mathcal{L}[p,q],x)} wkłada element x na listę [p,q]. Funkcja empty([p,q]) zwraca wartość true gdy lista jest pusta, oraz false w przeciwnym wypadku. Zwróćmy uwagę, że elementami każdej z list [p,q] są pary stanów (s,t)S×S.

Algorytm



{Minimalizuj3 -- algorytm minimalizacji wykorzystujący relację }

[1] Wejście: 𝒜=(S,A,f,s0,T) -- automat

Wyjście: 𝒜=(S,A,f,s0,T) -- automat minimalny taki, że L(𝒜)=L(𝒜).

for each pT

for each qST

zaznaczone[p,q]1;

initialize([p,q])

endfor

endfor

for each(p,q)(T×T)((ST)×(ST))

zaznaczone[p,q]0;

endfor

for each(p,q)(T×T)((ST)×(ST))

flagfalse

for each aA

if zaznaczone[f(p,a),f(q,a)]=1 flagtrue;

endif endfor

if flag=true

Oznacz(p,q); para (f(p,a),f(q,a)) była oznaczona dla pewnego a;

else

for each aA

if f(p,a)f(q,a)

włóż([p,q],(f(p,a),f(q,a)));

endif

endfor

endif

endfor

Parser nie mógł rozpoznać (nieznana funkcja „\slash”): {\displaystyle \displaystyle S' \leftarrow S \slash_\equiv} ; relacja jest dopełnieniem tabeli zaznaczone

for each Parser nie mógł rozpoznać (nieznana funkcja „\slash”): {\displaystyle \displaystyle [s]_{ \equiv} \in S \slash_\equiv}

for each aAf([s],a)[f(s,a)];

endfor

endfor

s0[s0];

T{[t]: tT};

return 𝒜=(S,A,f,s0,T);


Występujaca w algorytmie procedura Oznacz opisana jest poniżej.

Algorytm



[1]

procedure Oznacz(p,qS)

if zaznaczone[p,q]=1 { return} endif

{zaznaczone[p,q]1}

while not empty([p,q]) {Oznacz(zdejmij([p,q]))} endwhile

end procedure


Działanie algorytmu łatwo przedstawić na tabelce, która złożona jest z kwadratów -- pól, odpowiadających parom stanów automatu. Fakt znalezienia przez algorytm pary stanów rozróżnialnych zaznaczamy symbolem "x" w polu tabelki odpowiadającym tej parze, co wykorzystamy w przykładzie.

Przykład

Zminimalizujemy automat przedstawiony na rysunku Uzupelnic ja-lekcja5-w-rys4|, używając algorytmu Minimalizuj3.

RYSUNEK ja-lekcja5-w-rys4

Proces działania algorytmu i konstrukcji tabelki przedstawiony jest na poniższej animacji

TUTAJ ANIMACJA. Opis animacji znajduje się w pliku ja-lekcja5-w-anim1.pdf. Wygląd ekranu animacji znajduje się w pliku ja-lekcja5-w-anim1.jpg.

Wypełniona tabelka po zakończeniu działania algorytmu przedstawiona jest na rysunku Uzupelnic ja-lekcja5-w-rys5|.

RYSUNEK ja-lekcja5-w-rys5.

Z tabelki odczytujemy, że stanami równoważnymi są stany s1,s5, stany s2,s8 oraz stany s4,s6. Automat minimalny przedstawiony jest na rysunku Uzupelnic ja-lekcja5-w-rys6|.

RYSUNEK ja-lekcja5-w-rys6.