4.2. Automat skończony
Transkrypt
4.2. Automat skończony
4.2. Automat skończony Przykład: Rozważamy język nad alfabetem binarnym T = {0, 1} składający się z łańcuchów zero-jedynkowych o tej własności, że liczba zer w każdym łańcuchu jest parzysta i liczba jedynek w każdym łańcuchu też jest parzysta. Wszystkie łańcuchy binarne możemy podzielić na cztery grupy: S – łańcuchy z parzystą liczbą jedynek i parzystą liczbą zer, A – łańcuchy z parzystą liczbą jedynek i nieparzystą liczbą zer, B – łańcuchy z nieparzystą liczbą jedynek i parzystą liczbą zer, C – łańcuchy z nieparzystą liczbą jedynek i nieparzystą liczbą zer. Analizujemy łańcuch zero-jedynkowy symbol po symbolu od lewej strony. Przed rozpoczęciem analizy jesteśmy w grupie S (łańcuch pusty zawiera zero jedynek i tyleż zer, więc liczba jedynek i liczba zer w tym łańcuchu są parzyste). Jeśli pierwszym symbolem jest jedynka – przechodzimy do grupy A (wtedy liczba jedynek jest nieparzysta, a liczba zer jest dalej parzysta), zaś jeśli pierwszym symbolem jest zero – przechodzimy do grupy B (wtedy liczba zer jest nieparzysta, a liczba jedynek jest dalej parzysta). Zapisujemy to w postaci produkcji: S → 1A | 0B Dalej analizujemy podobnie kolejne symbole łańcucha. Np. jeśli jesteśmy w grupie A i przeczytamy zero – przechodzimy do grupy C, w której zarówno liczba jedynek, jak i zer są nieparzyste (odpowiedni zapis: A → 0C). Proces analizy łańcucha można zilustrować w postaci poniższego grafu: 1 S A 1 parz. 0 parz. 1 nieparz. 0 parz. 0 1 0 0 0 1 B C 1 parz. 0 nieparz. 1 nieparz. 0 nieparz. 1 Wreszcie, gdy przeczytaliśmy cały łańcuch, sprawdzamy, czy zatrzymaliśmy się w grupie S. Jeśli tak – badany łańcuch spełnia nałożony nań warunek parzystej liczby jedynek i parzystej liczby zer. Wówczas należy wyeliminować z wyprowadzenia symbol S (odpowiedni zapis: S → ε). Ostatecznie gramatyka naszego języka ma postać: S → 1A | 0B | ε A → 1S | 0C B → 1C | 0S C → 1B | 0A Opisana procedura i zamieszczony powyżej rysunek ilustrują właściwie nie tyle proces konstruowania gramatyki dla pewnego języka, co proces odpowiadania na pytanie: czy dany łańcuch jest słowem należącym do danego języka. Jest to pewien (w naszym przypadku deterministyczny) algorytm postępowania, polegający na czytaniu badanego łańcucha symbol po symbolu i przechodzenia od jednego stanu do drugiego. Stany reprezentowane są przez kółka (węzły grafu), zaś przejścia pomiędzy stanami, to skierowane krawędzie, opisane (etykietowane) odpowiednimi symbolami alfabetu, z którego pochodzą symbole łańcucha. Jeden ze stanów jest wyróżniony jako stan początkowy (na rysunku – jest to stan oznaczony krótką strzałką dochodząc do niego z zewnątrz). Od tego stanu zawsze rozpoczynamy „wędrówkę” po grafie. Niektóre stany są traktowane jako stany końcowe – akceptujące (są one zaznaczone kółkami rysowanymi podwójną linią). Jeśli w trakcie naszej „wędrówki” po grafie zatrzymamy się w takim stanie, to akceptujemy badany łańcuch, jeśli zatrzymamy się w stanie nie będącym stanem końcowym – nie akceptujemy analizowanego łańcucha. Zatrzymanie się w naszym przypadku może być tylko spowodowane przeczytaniem badanego słowa do końca i stwierdzeniem, że już nic nie pozostało do przeczytania. Opisany powyżej algorytm nosi nazwę automatu skończonego (a dokładnie deterministycznego i zupełnego automatu skończonego). Okazuje się, że językami, które mogą być akceptowane przez tego typu automaty są języki regularne (zbiory regularne) i tylko one. Pewnym potwierdzeniem tej reguły jest fakt, że otrzymana w naszym przykładzie gramatyka jest gramatyką prawostronnie liniową. Skąd pochodzi nazwa automat? Możemy sobie wyobrazić, że nasz algorytm to „pseudourządzenie” dysponujące taśmą wejściową, na której zapisany jest badany łańcuch i nad którą może poruszać się głowica odczytująca pojedyncze symbole. Głowica ma tylko możliwość ruchu w prawo, tzn. od początku łańcucha ku jego końcowi. Każdorazowo głowica przesuwa się tylko o jeden symbol. W każdym kroku głowica „widzi” tylko jeden symbol na taśmie. W każdym momencie automat charakteryzowany jest przez aktualny stan (liczba możliwych stanów jest skończona). Automat posiada swoje sterowanie (jest ono zdefiniowane przez tzw. funkcję przejść automatu), które na podstawie aktualnego stanu i symbolu czytanego przez głowicę, określa następny stan w następnym kroku. Początkowo głowica ustawiona jest na pierwszym symbolu badanego łańcucha, zaś automat znajduje się w stanie początkowym. Każdy krok pracy automatu to: odczytanie przez głowicę symbolu z taśmy wejściowej, określenie nowego stanu i przesunięcie głowicy o jedną pozycję w prawo na taśmie wejściowej. Kroki są powtarzane tak długo, jak długo można. Automat zatrzyma się, gdy np. przeczytał wejście do końca lub z innych powodów nie jest w stanie wykonać żadnego kroku. Zatrzymanie się automatu w stanie należącym do grupy stanów końcowych oznacza akceptację czytanego łańcucha i stwierdzenie, że łańcuch zapisany na taśmie wejściowej jest słowem należącym do języka akceptowanego przez automat. Zatrzymanie się naszego automatu (w jego wersji deterministycznej i zupełnej) w stanie nie będącym stanem końcowym oznacza brak akceptacji badanego łańcucha. Formalna definicja automatu skończonego Automatem skończonym nazywamy piątkę A = < T, Q, F, qo, δ >, gdzie: T – zbiór symboli terminalnych (alfabet wejściowy) Q – zbiór stanów, #Q < ∞ F ⊆ Q – zbiór stanów końcowych qo ∈ Q – stan początkowy δ – funkcja przejścia δ: Q × ( T ∪ {ε}) ! 2Q a b b a b a b a b b konfiguracja (q i,bbabababb) qi a b b przed wykonaniem kroku a b a b a b b konfiguracja (q j,bbabababb) qi po wykonaniu kroku Konfiguracja automatu to dwójka: (q, w), gdzie q jest aktualnym stanem, zaś w jest nieprzeczytaną przez automat częścią słowa zapisanego na taśmie wejściowej W T* q Q Konfiguracja początkowa : q0 Konfiguracja końcowa ( akceptująca ): q F stan końcowy Wyprowadzenie bezpośrednie: ( q, ax ) "A ( q’, x ) gdzie: q,q’∈Q, a∈( T ∪ {ε}), x∈T* , q’∈ δ(q,a) Wyprowadzenia pośrednie "A+ i "A* są odpowiednio przechodnim oraz przechodnim i zwrotnym domknięciem relacji wyprowadzenia bezpośredniego "A: (q,w) "A+ (q’ ,w’) ⇔ ∃ po, ... ,pn (pi – konfiguracje), takie że: qo=(q,w), qn= (q’ , w’), pi "A pi+1 dla i=0,1,...,n-1 * (q,w) "A (q’ , w’) ⇔ (q,w) "A+ (q’ ,w’) ∪ (q,w) = (q’ , w’) Konfiguracja blokująca : (q,w) jest blokująca ⇔! ¬ (∃(q’ ,w’)) ((q,w) "A (q’ ,w’)) x∈T* jest słowem akceptowanym przez automat A (skończony) ⇔ (∃q∈F) ((qo ,x) "A* (q,ε)) Język L jest akceptowany przez automat A ( co oznaczamy L(A) ) ⇔ L = L(A) = { x∈ T* | x jest akceptowane przez A } Przykład : Język regularny opisany wyrażeniem regularnym (a|b)*abb T={a,b} Q={q0, q1 ,q2 ,q3 ) F={q3} Q0 – stan początkowy δ - funkcja przejścia: Stan q0 q1 q2 a { q0 , q1 } ∅ ∅ b {q0} {q2} {q3} a start q0 a q1 b q2 b Analizowane słowo : aabb ∈ L( (a|b)*abb ) Możliwe wyprowadzenia: b q3 ( q0 , aabb ) " ( q0,abb) " ( q1,bb ) " ( q2,b ) " ( q3, ε) – konfiguracja końcowa akceptująca) ( q0 , aabb ) " ( q0,abb) " ( q0,bb ) " ( q0,b ) " ( q0, ε) – konfiguracja blokująca bo q0 ∉F ( q0 , aabb ) " ( q1,abb) – konfiguracja blokująca, słowo nie zostało przeczytane do końca Automat nie jest deterministyczny. Jednak istnieje wyprowadzenie (ciąg kroków), które doprowadza do konfiguracji akceptującej, więc zgodnie z definicją automat akceptuje to słowo. Własności automatu skończonego Automat skończony A jest zupełny ⇔ (∀a∈T) (∀q∈Q) (#δ(q,a) ≥ 1 Automat skończony A jest deterministyczny ⇔ (i) (∀q∈Q) (#δ(q,ε) = 0) oraz (ii) (∀a∈T) (∀q∈Q) (#δ(q,a) ≤ 1) Automat skończony A jest deterministyczny i zupełny ⇔ (i) (∀q∈Q) (#δ(q,ε) = 0) oraz (ii) (∀a∈T) (∀q∈Q) (#δ(q,a) = 1) Automat skończony zupełny nazywamy automatem Rabina-Scotta. Automat skończony, deterministyczny i zupełny nazywamy deterministycznym automatem Rabina-Scotta Przykład: Deterministyczny zupełny automat akceptujący język opisany wyrażeniem regularnym (a|b)*abb T={a,b} Q = { 0, 1 , 2 , 3, } a F={3} Przypomnienie poprzedniego przykładu : q0 = 0 automat niederministyczny i niezupełny δ - funkcja przejścia: Stan a b a b b 0 1 2 3 start 0 1 0 1 1 2 2 1 3 b 3 1 0 b b 0 a 1 a b 2 a a b 3