Teorie jazyku˚ a automatu - RNDr. Šárka Vavrečková, Ph.D.
Transkript
Šárka Vavrečková Teorie jazyků a automatů II Sbı́rka úloh pro cvičenı́ Slezská univerzita v Opavě Filozoficko-přı́rodovědecká fakulta Ústav informatiky Opava, poslednı́ aktualizace 1. února 2012 Anotace: Tento dokument obsahuje přı́klady ke cvičenı́m z předmětů Teorie jazyků a automatů II. Studenti zde najdou řešené přı́klady s podrobně popsaným postupem a neřešené přı́klady, na kterých si mohou sami postupy procvičit. Teorie jazyků a automatů II – sbı́rka přı́kladů pro cvičenı́ RNDr. Šárka Vavrečková, Ph.D. Dostupné na: http://fpf.slu.cz/~vav10ui/formal.html Ústav informatiky Filozoficko-přı́rodovědecká fakulta Slezská univerzita v Opavě Bezručovo nám. 13, 746 01 Opava Sázeno v systému LATEX Tato inovace předmětu Teorie jazyků a automatů II je spolufinancována Evropským sociálnı́m fondem a Státnı́m rozpočtem ČR, projekt č. CZ.1.07/2.3.00/09.0197, „Posı́lenı́ konkurenceschopnosti výzkumu a vývoje informačnı́ch technologiı́ v Moravskoslezském kraji“. Předmluva Tato skripta jsou určena pro studenty informatických oborů na Ústavu informatiky Slezské univerzity v Opavě, do cvičenı́ předmětu Teorie jazyků a automatů II, jde vlastně o jakousi cvičebnici a sbı́rku přı́kladů. Navazujeme na obdobná skripta pro cvičenı́ z předmětu Teorie jazyků a automatů I, tedy předpokládajı́ se již základnı́ znalosti v oblasti teoretické informatiky, ale ve skutečnosti se oboje skripta tematicky poněkud překrývajı́. Je to z toho důvodu, že během několika let procházejı́ oba předměty rozsáhlými změnami, a snahou je při přeskupovánı́ témat nepotrhat souvislosti. Ve skriptech se použı́vajı́ následujı́cı́ barevné ikony: • Nové pojmy a popisy postupů, nacházejı́ se obvykle na začátku kapitol a sekcı́. Sloužı́ k připomenutı́ pojmů a postupů probı́raných na přednáškách, abychom na cvičenı́ch při řešenı́ přı́kladů měli vše potřebné po ruce. • Některé části textu jsou označeny fialovou ikonou, což znamená, že jde o nepovinné úseky, které nejsou probı́rány. Jejich účelem je dobrovolné rozšı́řenı́ znalostı́ studentů o pokročilá témata, na která obvykle při výuce nezbývá moc času. • Červená je ikona pro upozorněnı́ a poznámky. Přı́klad 0.1 P L Takto vypadá prostředı́ s přı́kladem. Čı́slo přı́kladu je 0.1. Úkol 0.1 Otázky a úkoly, náměty na vyzkoušenı́, které se doporučuje při procvičovánı́ učiva provádět, jsou uzavřeny v tomto prostředı́. Pokud je v prostředı́ vı́ce úkolů, jsou čı́slovány. iii C Obsah 1 2 3 Konečné automaty 1 1.1 Opakovánı́ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Determinismus a úplnost konečných automatů . . . . . . . . . . . . . . . . . 4 1.3 Odstraněnı́ nepotřebných stavů . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Regulárnı́ jazyky 11 2.1 Pumping lemma pro regulárnı́ jazyky . . . . . . . . . . . . . . . . . . . . . . 11 2.2 Uzávěrové vlastnosti – operace nad regulárnı́mi jazyky . . . . . . . . . . . . 16 2.2.1 Sjednocenı́ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.2.2 Zřetězenı́ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.2.3 Iterace (Kleeneho uzávěr) . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.2.4 Pozitivnı́ iterace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2.5 Zrcadlový obraz (reverze) . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.2.6 Průnik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.2.7 Homomorfismus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.3 Sestrojenı́ konečného automatu podle regulárnı́ho výrazu . . . . . . . . . . . 35 2.4 Vytvořenı́ regulárnı́ho výrazu podle konečného automatu . . . . . . . . . . 37 2.5 Minimalizace a normovánı́ konečného automatu . . . . . . . . . . . . . . . . 40 2.5.1 Minimálnı́ konečný automat . . . . . . . . . . . . . . . . . . . . . . . 40 2.5.2 Normovaný konečný automat . . . . . . . . . . . . . . . . . . . . . . 45 K zásobnı́kovým automatům 48 3.1 Využitı́ uzávěrových vlastnostı́ při konstrukci automatu . . . . . . . . . . . 48 3.2 Uzávěrové vlastnosti jako kritérium bezkontextovosti . . . . . . . . . . . . . 51 iv v 4 5 Regulárnı́ gramatiky 52 4.1 Opakovánı́ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.2 Normálnı́ forma regulárnı́ch gramatik . . . . . . . . . . . . . . . . . . . . . . 54 Bezkontextové gramatiky 58 5.1 Derivačnı́ strom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.2 Úpravy bezkontextových gramatik . . . . . . . . . . . . . . . . . . . . . . . . 60 5.2.1 Redukce gramatiky . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 5.2.2 Převod na nezkracujı́cı́ bezkontextovou gramatiku . . . . . . . . . . 62 5.2.3 Odstraněnı́ jednoduchých pravidel . . . . . . . . . . . . . . . . . . . . 66 5.2.4 Gramatika bez cyklu a vlastnı́ gramatika . . . . . . . . . . . . . . . . 69 5.2.5 Levá a pravá rekurze . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Normálnı́ formy bezkontextových gramatik . . . . . . . . . . . . . . . . . . . 76 5.3.1 Chomského normálnı́ forma . . . . . . . . . . . . . . . . . . . . . . . 76 5.3.2 Greibachové normálnı́ forma . . . . . . . . . . . . . . . . . . . . . . . 78 Kritéria bezkontextovosti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 5.4.1 Pumping lemma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 5.4.2 Parikhův vektor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.3 5.4 6 Jazyky typu 0 88 6.1 Turingův stroj . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 6.1.1 Co je to Turingův stroj – pojmy a značenı́ . . . . . . . . . . . . . . . . 88 6.1.2 Navrhujeme Turingův stroj pro daný jazyk . . . . . . . . . . . . . . . 89 Gramatiky typu 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 6.2.1 Návrh gramatiky typu 0 . . . . . . . . . . . . . . . . . . . . . . . . . . 95 6.2.2 Kurodova normálnı́ forma . . . . . . . . . . . . . . . . . . . . . . . . . 97 6.2 vi Kapitola 1 Konečné automaty 1.1 Opakovánı́ Nejdřı́v si zopakujeme něco z toho, co jsme se naučili v minulém semestru. Základy musı́me znát dokonale, protože na nich budeme stavět po celý semestr. Přı́klad 1.1 Jazyky zadané regulárnı́m výrazem zapı́šeme v množinovém zápisu. Ke každému jazyku napı́šeme množinu všech jeho slov, která jsou kratšı́ než 5. L1 = (ab)∗ Množinový zápis: L1 = {(ab)n : n ≥ 0} Slova kratšı́ než 5: {w ∈ L1 : |w| < 5} = {ε, ab, abab} Mnemotechnická pomůcka: za index v množinovém zápisu dosazujeme postupně čı́sla od nejnižšı́ hodnoty zadaného rozsahu, tj. od 0. Všimněte si symbolického zápisu – nelze napsat rovnı́tko mezi jazyk L1 a množinu slov tohoto jazyka kratšı́ch než 5, je třeba to zapsat jinak. P L2 = a∗ b∗ a Množinový zápis: L2 = {am bn a : m, n ≥ 0} Všimněte si: je třeba použı́t dva navzájem nezávislé indexy! Slova kratšı́ než 5: {w ∈ L2 : |w| < 5} = a, aa, ba, aaa, aba, bba, a4 , a2 ba, ab2 a, b3 a Mnemotechnická pomůcka: za indexy m a n v množinovém zápisu dosazujeme čı́sla navzájem nezávisle (m = n = 0, m = 1 a n = 0, m = 0 a n = 1, atd.). Pozor, prázdné slovo do jazyka nepatřı́! 1 P 1.1 OPAKOVÁNÍ 2 Přı́klad 1.2 Určı́me typ zadaného jazyka v Chomského hierarchii. Pokud se jedná o regulárnı́ jazyk, vytvořı́me ekvivalentnı́ regulárnı́ výraz. Vypı́šeme všechna slova jazyka kratšı́ než 5. L1 = (aa)i (bb)j : i, j ≥ 0 Jde o regulárnı́ jazyk. Ekvivalentnı́ regulárnı́ výraz: L1 = (aa)∗ (bb)∗ Slova kratšı́ než 5: {w ∈ L1 : |w| < 5} = ε, a2 , b2 , a2 b2 , a4 , b4 L2 = (aa)i (bb)i : i ≥ 0 Tento jazyk je sice podobný předchozı́mu, ale nenı́ to regulárnı́ jazyk (je bezkontextový). Proto nelze vytvořit ekvivalentnı́ regulárnı́ výraz. Jak poznáme, že nenı́ regulárnı́? Pokud je třeba (při zpracovánı́ slov jazyka automatem) synchronizovat dvě nebo dokonce vı́ce částı́ slova (totéž pı́smeno indexu se vyskytuje na vı́ce mı́stech v množinové reprezentaci), nejde o regulárnı́ jazyk. To lze dokázat napřı́klad pomocı́ Pumping lemma (budeme brát později). Slova kratšı́ než 5: {w ∈ L2 : |w| < 5} = ε, a2 b2 Srovnejte s obdobnou množinou pro jazyk L1 z tohoto přı́kladu. L3 = 0i 1i 0i : i ≥ 1 Jde o jazyk typu 1 v Chomského hierarchii, nenı́ to regulárnı́ ani bezkontextový jazyk. Slova kratšı́ než 5: {w ∈ L3 : |w| < 5} = {010} Přı́klad 1.3 Vypı́šeme všechna slova jazyka kratšı́ než 5. n L1 = ai bj : 1 ≤ i ≤ j ∪ ck : k ≤ 3 o Slova kratšı́ než 5: {w ∈ L1 : |w| < 5} = ab, ab2 , a2 b2 , ε, c, c2 , c3 Poměrně častou chybou bývá „otočenı́“ relačnı́ho znaménka. V předpisu druhé množiny sjednocenı́ máme symbol „menšı́ nebo rovno“, ne naopak, musı́me tedy vypsat slova o délce 0 až 3. L2 = ai bj : 1 ≤ i ≤ j ∩ ai bj : 1 ≤ j ≤ i Když se pozorně podı́váme na množiny, jejichž průnikem je jazyk L2 , zjistı́me, že tento jazyk můžeme zapsat jednodušeji, protože prvnı́ množina nám dává podmı́nku „symbolů b má být stejně nebo vı́ce než symbolů a“, druhá množina předepisuje „symbolů b má být stejně nebo méně než symbolů a“: L2 = ai bi : i ≥ 1 Slova kratšı́ než 5: {w ∈ L2 : |w| < 5} = ab, a2 b2 Pozor na rozdı́l mezi sjednocenı́m a průnikem, záměna těchto dvou množinových operacı́ je poměrně častou chybou. P 1.2 DETERMINISMUS A ÚPLNOST KONEČNÝCH AUTOMATŮ 3 Přı́klad 1.4 Pro zadané jazyky sestrojı́me konečný automat ve všech třech reprezentacı́ch. L1 = ab∗ Stavový diagram: Tabulka přechodů: δ-funkce: A1 = ({q0 , q1 }, {a, b}, δ, q0 , {q1 }) δ(q0 , a) = q1 δ(q1 , b) = q1 a → ← q0 q1 b a q1 q0 b q1 q1 Všimněte si, že u δ-funkce je nutné uvádět plnou specifikaci, aby bylo zřejmé, který stav je počátečnı́ a které stavy jsou koncové, u jiných reprezentacı́ jsou tyto informace zaznamenány jinak. L2 = ab∗ + ε Tabulka přechodů: δ-funkce: A2 = ({q0 , q1 }, {a, b}, δ, q0 , {q0 , q1 }) δ(q0 , a) = q1 δ(q1 , b) = q1 Úkol a ↔ ← q0 q1 Stavový diagram: b a q1 q0 b q1 q1 1.1 Srovnejte automaty pro jazyky L1 a L2 z tohoto přı́kladu a zjistěte, jaké důsledky mělo přidánı́ prázdného slova k jazyku u jednotlivých reprezentacı́ automatu. C Přı́klad 1.5 Pro zadaný jazyk sestrojı́me konečný automat ve všech třech reprezentacı́ch. L = a∗ b + b∗ a δ-funkce: Tabulka přechodů: A = ({q0 , q1 , q2 }, {a, b}, δ, q0 , {q1 , q2 }) δ(q0 , a) = q1 δ(q0 , b) = q2 δ(q1 , b) = q1 δ(q2 , a) = q2 → ← ← q0 q1 q2 a b q1 q2 q1 q2 Stavový diagram: b q1 a q0 a b q2 Všimněte si, jakým způsobem se v reprezentacı́ch automatu projevila operace sčı́tánı́ regulárnı́ch (pod)výrazů. Jazyk lze zapsat také následovně – v množinovém zápisu s využitı́m operace sjednocenı́ množin: L = abi : i ≥ 0 ∪ bai : i ≥ 0 P 1.2 DETERMINISMUS A ÚPLNOST KONEČNÝCH AUTOMATŮ 1.2 4 Determinismus a úplnost konečných automatů Nedeterministický konečný automat je takový automat, kde v alespoň jednom stavu lze na některý signál reagovat vı́ce různými způsoby. Platı́ (viz přednášky), že třı́da jazyků rozpoznávaných deterministickými konečnými automaty a třı́da jazyků rozpoznávaných těmi nedeterministickými jsou ekvivalentnı́, a tedy existuje postup, jak k nedeterministickému automatu sestrojit ekvivalentnı́ deterministický a naopak. Vytvořenı́ nedeterministického podle deterministického je triviálnı́, stačı́ v přechodové funkci přidat množinové závorky. Opačný postup již nenı́ triviálnı́, postupujeme takto: • v ekvivalentnı́m deterministickém automatu budou stavy určeny množinami stavů původnı́ho nedeterministického automatu, • pro každou dvojici stav–signál v původnı́m automatu vytvořı́me množinu stavů, které jsou cı́lem přechodů vedoucı́ch z daného stavu na daný signál, • pro každý stav nového automatu (tj. množinu stavů původnı́ho automatu) určı́me přechody pomocı́ operace sjednocenı́ množin. Postup si připomeneme na přı́kladu. Přı́klad 1.6 Je dán nedeterministický konečný automat. Sestrojı́me ekvivalentnı́ deterministický. δ-funkce: Tabulka přechodů: AN = ({0, 1, 2}, {a, b}, δN , 0, {2}) δN (0, a) = {1, 2} δN (0, b) = {1} δN (1, a) = {0} δN (2, a) = {2} δN (2, b) = {0, 1} → ← ← 0 1 2 a b 1,2 0 2 1 Stavový diagram: 0,1 b q1 a q0 a b q2 Nejdřı́v určı́me stavy nového automatu. To budou množiny obsahujı́cı́ stavy původnı́ho automatu: n {0}, {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2} o Počátečnı́ stav bude {0}, množina koncových stavů bude obsahovat právě ty množiny původnı́ch stavů, kde alespoň jeden původnı́ stav je koncový, tj. n o {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2} . Zbývá určit přechodovou funkci – postupujeme podle předpisu: δD {q1 , . . . , qk }, a = [ δN (qi , a) i=1,...,k kde δD a δN jsou přechodové funkce nového deterministického a původnı́ho nedeterministického automatu, qi jsou stavy původnı́ho automatu a symbol a je některým prvkem abecedy (a ∈ Σ). P 1.2 DETERMINISMUS A ÚPLNOST KONEČNÝCH AUTOMATŮ 5 Předpis nám řı́ká, že budeme jednoduše sjednocovat množiny, tedy: AD = n o {0}, {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2} , {a, b}, δD , {0}, n o {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2} δD ({0}, a) = {1, 2} δD ({0}, b) = {1} δD ({1}, a) = {0} δD ({2}, a) = {2} δD ({2}, b) = {0, 1} δD ({0, 1}, a) = {1, 2} ∪ {0} = {0, 1, 2} δD ({0, 1}, b) = {1} ∪ ∅ = {1} δD ({0, 2}, a) = {1, 2} ∪ {2} = {1, 2} δD ({0, 2}, b) = {1} ∪ {0, 1} = {0, 1} δD ({1, 2}, a) = {0} ∪ {2} = {0, 2} δD ({1, 2}, b) = ∅ ∪ {0, 1} = {0, 1} δD ({0, 1, 2}, a) = {1, 2} ∪ {0} ∪ {2} = {0, 1, 2} δD ({0, 1, 2}, b) = {1} ∪ ∅ ∪ {0, 1} = {0, 1} V tabulce přechodů: → ← ← ← ← ← ← {0} {1} {2} {0, 1} {0, 2} {1, 2} {0, 1, 2} Stavový diagram: b 0 a a b 1, 2 a a a b {1, 2} {0} {2} {1} {1, 2} ∪ {0} = {0, 1, 2} {1, 2} ∪ {2} = {1, 2} {0} ∪ {2} = {0, 2} {0, 1} {1, 2} ∪ {0} ∪ {2} = {0, 1, 2} b 1 b a 0, 1 b b 0, 2 a 2 0,1,2 a {1} ∪ ∅ = {1} {1} ∪ {0, 1} = {0, 1} ∅ ∪ {0, 1} = {0, 1} {1} ∪ ∅ ∪ {0, 1} = {0, 1} Je zřejmé, že stav {2} je ve výsledném deterministickém automatu nedosažitelný (nevede do něj žádná cesta z počátečnı́ho stavu), proto lze tento stav odebrat při zachovánı́ rozpoznávaného jazyka. Postup můžeme zkrátit – nové stavy (množiny původnı́ch stavů) lze vytvářet „dynamicky podle potřeby“, tedy pokud se v některé buňce uvnitř tabulky (resp. za rovnı́tkem v předpisu δ-funkce) nacházı́ množina původnı́ch stavů, kterou ještě nemáme v ohodnocenı́ žádného řádku (resp. v parametru δ-funkce), přidáme nový řádek s tı́mto ohodnocenı́m a sestrojı́me operacı́ sjednocenı́ množin obsah buněk na řádku (resp. výsledek δ-funkce), to provádı́me rekurzı́vně tak dlouho, dokud nebudou vytvořeny všechny přechody. P 1.2 DETERMINISMUS A ÚPLNOST KONEČNÝCH AUTOMATŮ 6 Totálnı́ (úplný) konečný automat je takový deterministický konečný automat, kde v každém stavu lze reagovat na jakýkoliv signál z abecedy. Zúplněnı́ konečného automatu (tj. vytvořenı́ ekvivalentnı́ho totálnı́ho automatu) se provádı́ takto: 1. vytvořı́me deterministický automat ekvivalentnı́ původnı́mu nedeterministickému, 2. přidáme nový stav (obvykle se označuje ∅), tento stav bude fungovat jako „odpadkový koš“, nesmı́ patřit do množiny koncových stavů, 3. pokud v některém stavu nelze reagovat na některý signál, vytvořı́me pro tento signál nový přechod vedoucı́ do stavu ∅, 4. totéž provedeme i pro stav ∅, i v tomto stavu je třeba reagovat na jakýkoliv signál. Přı́klad 1.7 V přı́kladu 1.6 jsme vytvořili deterministický konečný automat ekvivalentnı́ původnı́mu nedeterministickému. Ukážeme si jeho zúplněnı́, tj. vytvořı́me ekvivalentnı́ totálnı́ (úplný) automat. ADT = n o {0}, {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2}, ∅ , {a, b}, δD , {0}, n {1}, {2}, {0, 1}, {0, 2}, {1, 2}, {0, 1, 2} δD ({0, 1}, a) = {1, 2} ∪ {0} = {0, 1, 2} δD ({0, 1}, b) = {1} ∪ ∅ = {1} δD ({0, 2}, a) = {1, 2} ∪ {2} = {1, 2} δD ({0, 2}, b) = {1} ∪ {0, 1} = {0, 1} δD ({1, 2}, a) = {0} ∪ {2} = {0, 2} δD ({1, 2}, b) = ∅ ∪ {0, 1} = {0, 1} δD ({0}, a) = {1, 2} δD ({0}, b) = {1} δD ({1}, a) = {0} δD ({2}, a) = {2} δD ({2}, b) = {0, 1} δD ({1}, b) = ∅ δD (∅, a) = ∅ δD (∅, b) = ∅ δD ({0, 1, 2}, a) = {1, 2} ∪ {0} ∪ {2} = {0, 1, 2} δD ({0, 1, 2}, b) = {1} ∪ ∅ ∪ {0, 1} = {0, 1} Tabulka přechodů: → ← ← ← ← ← ← {0} {1} {2} {0, 1} {0, 2} {1, 2} {0, 1, 2} ∅ o Stavový diagram: a b {1, 2} {0} {2} {0, 1, 2} {1, 2} {0, 2} {0, 1, 2} ∅ {1} ∅ {0, 1} {1} {0, 1} {0, 1} {0, 1} ∅ b 0 a a b 1, 2 a a b 1 b a 0, 1 b b 0, 2 a, b ∅ 0,1,2 a P 1.3 ODSTRANĚNÍ NEPOTŘEBNÝCH STAVŮ 7 Proč jsou vlastnosti determinismu a úplnosti důležité? Protože pouze pro to, co lze popsat deterministickým postupem, existuje algoritmus a lze to naprogramovat, a úplnost je důležitá při programovánı́ postupů s předvı́datelným chovánı́m – program by měl reagovat předvı́datelně v každé situaci, i kdyby to mělo být třeba jen vypsánı́ chybového hlášenı́. Nový stav přidávaný během zúplňovánı́ si můžeme představit jako „chybový stav“ sloužı́cı́ k ošetřenı́ chyb. Úkol 1.2 1. Uvedené nedeterministické konečné automaty reprezentované tabulkami převed’te na ekvivalentnı́ deterministické. Ke každému vytvořte stavový diagram původnı́ho nedeterministického automatu i vytvořeného deterministického a porovnejte. Všechny vytvořené automaty zúplňte. a ↔ ← A B C b B A B c B, C C → ← ← q0 q1 q2 0 1 q0 , q1 q1 q0 , q1 q2 → ← q0 q1 q2 a b q1 , q2 q2 q0 q1 q2 L C c q1 q2 2. K zadaným konečným automatům sestrojte ekvivalentnı́ deterministické totálnı́ automaty. Před zúplněnı́m můžete odstranit nepotřebné stavy. Výsledný automat napište ve všech třech formách (δ-funkce, tabulka přechodů, stavový diagram). A1 = ({0, 1, 2}, {a, b}, δ, 0, {2}) δ(0, b) = {1, 2} δ(1, a) = {0, 1} δ(1, b) = {0} δ(2, a) = {1} 1.3 A2 = ({0, 1, 2}, {a, b}, δ, 0, {2}) δ(0, a) = {1, 2} δ(1, a) = {1} δ(1, b) = {0, 2} δ(2, b) = {1} Odstraněnı́ nepotřebných stavů Nepotřebné stavy jsou stavy, které nejsou použity při žádném úspěšném výpočtu (tj. končı́cı́m v koncovém stavu). Jedná se o stavy • nedosažitelné – neexistuje k nim cesta z počátečnı́ho stavu, • nadbytečné – neexistuje cesta z tohoto stavu do jakéhokoliv koncového stavu. S odstraňovánı́m (lépe řečeno ignorovánı́m, neuvedenı́m či vypuštěnı́m) prvnı́ho typu nepotřebných stavů – nedosažitelných – jsme se setkali už u zkráceného postupu pro převod nedeterministického automatu na deterministický. Pokud nové řádky tabulky přechodů P 1.3 ODSTRANĚNÍ NEPOTŘEBNÝCH STAVŮ 8 tvořı́me jen pro takové množiny původnı́ch stavů, které se již vyskytly v některé buňce, většinu nedosažitelných stavů (ale ne vždy všechny) automaticky odstraňujeme. Když chceme odstranit nepotřebné stavy, vždy začı́náme nedosažitelnými stavy a až potom odstranı́me nadbytečné. V obou přı́padech postupujeme rekurzı́vně, a to bud’ podle stavového diagramu nebo podle tabulky přechodů. • nedosažitelné stavy: jako bázi (základnı́ množinu) zvolı́me S0 = {q0 } (obsahuje pouze počátečnı́ stav), dalšı́ prvky přidáváme „ve směru šipek“ v stavovém diagramu, resp. v tabulce přechodů ve směru označenı́ řádku → obsah buněk na řádku, postupně vytvářı́me množinu všech stavů, do kterých vede cesta z počátečnı́ho stavu o délce max. 0, 1, . . . , n kroků (toto čı́slo je dolnı́ index u označenı́ vytvářené množiny Si ); • nadbytečné stavy: jako bázi naopak zvolı́me množinu koncových stavů – E0 = F , dalšı́ prvky přidáváme „proti směru šipek“ v stavovém diagramu, resp. ve směru obsah některé buňky tabulky přechodů → označenı́ řádku, vytvářı́me množinu všech stavů, ze kterých vede cesta do některého koncového stavu o délce max. 0, 1, . . . , n kroků. Formálnı́ zápis pro automat A = (Q, Σ, δ, q0 , F ): • S0 = {q0 } Si = Si−1 ∪ {q : δ(p, a) = q, kde p ∈ Si−1 , a ∈ Σ} , pro i ≥ 1 • E0 = F Ei = Ei−1 ∪ {p : δ(p, a) = q, kde q ∈ Si−1 , a ∈ Σ} , pro i ≥ 1 Index i samozřejmě nejde do nekonečna, iteraci lze omezit hornı́ hranicı́ |Q|, tedy mohutnostı́ (počtem prvků) množiny stavů automatu (tj. 1 ≤ i ≤ |Q|). Přı́klad 1.8 V zadaném konečném automatu odstranı́me nedosažitelné a nadbytečné stavy. → q0 ← q1 q2 q3 ← q4 ← q5 a b q1 q1 q3 q3 q5 q5 q3 q2 q2 c q5 q0 q3 a q0 c b a q3 a a c q1 b b q2 c a a q5 q4 Odstranı́me nedosažitelné stavy (do kterých neexistuje cesta z počátečnı́ho stavu): S0 S1 S2 S3 = {q0 } = {q0 } ∪ {q1 , q3 } = {q0 , q1 , q3 } (ze stavu q0 vede přechod do q1 a q3 ) = {q0 , q1 , q3 } ∪ {q5 , q2 } = {q0 , q1 , q3 , q5 , q2 } = {q0 , q1 , q3 , q5 , q2 } = S2 (konec, v poslednı́m kroku žádný stav do množiny nepřibyl) 1.3 ODSTRANĚNÍ NEPOTŘEBNÝCH STAVŮ 9 V množině S3 nenı́ stav q4 , je tedy nedosažitelný z počátečnı́ho stavu a můžeme ho z automatu odstranit. V každé z množin Si , které jsme postupně vytvořili, najdeme všechny stavy, které jsou z počátečnı́ho stavu dosažitelné po nejvýše i krocı́ch. Dále zjistı́me, ze kterých stavů nevede cesta do koncových stavů. Budeme pracovat již s automatem po odstraněnı́ stavu q4 . E0 E1 E2 E3 = F = {q1 , q5 } = {q1 , q5 } ∪ {q0 } = {q1 , q5 , q0 } (do stavů z E0 vede přechod pouze ze stavu q0 ) = {q1 , q5 , q0 , q2 } = {q1 , q5 , q0 , q2 } = E2 V množině E3 nenı́ stav q3 , to znamená, že z tohoto stavu neexistuje žádná cesta do koncového a tedy když tento stav odstranı́me, neovlivnı́me výpočet žádného slova, které automat rozpoznává. Odstranı́me tento stav a také všechny přechody s nı́m přı́mo souvisejı́cı́. Po odstraněnı́ stavů, které jsme vyřadili s použitı́m množin Si a Ei , dostáváme následujı́cı́ konečný automat: a → q0 ← q1 q2 ← q5 Úkol q1 q1 b q2 c q5 q0 q5 a q0 c a c q1 b q2 a q5 1.3 1. Podle zadánı́ následujı́cı́ho automatu vytvořte tabulku přechodů a stavový diagram. Potom odstraňte nepotřebné stavy. Takto upravený automat pak zúplňte (převed’te na totálnı́ automat). A = ({A, B, C, D, E, F }, {0, 1, 2}, δ, A, {A, C}) s funkcı́ δ: δ(A, 0) = E δ(A, 1) = B δ(A, 2) = E δ(B, 0) = C δ(B, 1) = D δ(B, 2) = E δ(C, 0) = C δ(C, 1) = C δ(D, 2) = C δ(E, 1) = E δ(E, 2) = E δ(F, 0) = C δ(F, 1) = E δ(F, 2) = F C 1.3 ODSTRANĚNÍ NEPOTŘEBNÝCH STAVŮ 10 2. Ke konečnému automatu vpravo vytvořte zbývajı́cı́ dvě reprezentace – δ-funkci a tabulku přechodů. Potom odstraňte všechny nepotřebné stavy. a b A a c b D 3. Podle následujı́cı́ch konečných automatů určených tabulkami přechodů sestrojte stavové diagramy a pak odstraňte všechny nepotřebné stavy. ↔A B C D ←E a b c A C A B D C A B C B C →A B ←C ←D E a b c C A B B E B A A E D A E ↔A B C D ←E a b A C B A B B E C → q0 ← q1 ← q2 q3 q4 B a a C b a a F a b q4 q4 q3 q1 q1 q4 q3 b E a G c q0 q2 q3 4. Zamyslete se nad těmito přı́pady: jak by vypadal jazyk upraveného automatu (bez nepotřebných stavů), kdyby do množin Si nebyly zařazeny žádné koncové stavy? Jak by vypadal tento jazyk, kdyby do množin Ei nebyl zařazen počátečnı́ stav? Kapitola 2 Regulárnı́ jazyky 2.1 Pumping lemma pro regulárnı́ jazyky Pro každý regulárnı́ jazyk L platı́ tato vlastnost: vždy existuje přirozené čı́slo p > 0 takové, že pro každé slovo w ∈ L delšı́ než p (tj. |w| > p) existuje (alespoň jedno, přı́padně vı́ce) rozdělenı́ w = x · y · z (tj. na tři části) takové, že • |y| > 0 (v prostřednı́ části musı́ být alespoň jeden znak – signál), P • |y| ≤ p (délka prostřednı́ části slova je shora omezena, tedy konečná, v jejı́ specifikaci nesmı́me použı́t „index“), • x · (y)k · z ∈ L pro jakékoliv k ≥ 0 (když prostřednı́ část „pumpujeme“, výsledné slovo opět patřı́ do jazyka – y bud’ vyřadı́me pro k = 0 a nebo opakujeme 1×, 2×, 3×, atd.) Pumpovacı́ věta je implikace. Řı́ká nám, že když je jazyk regulárnı́, tak má danou vlastnost. Nenı́ nikde řečeno, že jazyk, který nenı́ regulárnı́, danou vlastnost nemá. Přı́klad 2.1 Nejdřı́v si ukážeme, jak ověřit, že regulárnı́ jazyk tuto vlastnost má. Postup si ukážeme na jazyku L = {ai bj : i, j ≥ 0} Pro tento jazyk dokážeme sestrojit konečný automat, tedy b a vı́me, že se jedná o regulárnı́ jazyk. Vlastnost popsanou v Pumb q1 q0 ping lemma si můžeme představit takto: • použijeme p rovno počtu stavů konečného automatu, tedy p = 2 • vezmeme jakékoliv slovo jazyka delšı́ než 2, napřı́klad w = a3 b (má délku 4) • w je delšı́ než počet stavů automatu, je tedy zřejmé, že některá část slova bude vyhodnocována v cyklu • při pohledu na stavový diagram automatu vidı́me, že tento cyklus jde přes jediný stav, a to q0 11 L 2.1 PUMPING LEMMA PRO REGULÁRNÍ JAZYKY 12 • vhodné rozdělenı́ může být napřı́klad w = (a) · (a2 ) · (b), po pumpovánı́ dostáváme slova (a) · (a2 )k · (b) = a1+2k b, pro jakékoliv k ≥ 0 tato slova patřı́ do jazyka L, • jiné vhodné rozdělenı́ je třeba w = (a2 ) · (a) · (b). Vidı́me, že prostřednı́ část slova vlastně odpovı́dá části slova, která procházı́ cyklem (nejméně jednou). Pumpovánı́ s indexem k pak odpovı́dá knásobnému průchodu cyklem v automatu. Platı́ pumping lemma pro všechny regulárnı́ jazyky včetně konečných? Samozřejmě ano. Pro každý konečný jazyk existuje konečný automat, který ho rozpoznává. Jako p si u konečného jazyka zvolı́me opět bud’ počet stavů tohoto automatu a nebo čı́slo ještě většı́. V Pumping lemma se pı́še „pro každé slovo w daného jazyka delšı́ než p . . . “, ale nepı́še se tam, že takové slovo opravdu musı́ existovat. Opravdu žádné takové neexistuje, protože kdyby existovalo slovo delšı́ než počet stavů automatu, musel by být v grafu stavového diagramu cyklus, a cyklus znamená nekonečný jazyk. Takže Pumping lemma platı́ i pro konečné jazyky. Úkol 2.1 U následujı́cı́ch regulárnı́ch jazyků vytvořte stavový diagram, vyberte „dostatečně dlouhé slovo“ – delšı́ než počet stavů automatu, určete některé vhodné rozdělenı́ slova na tři části podle podmı́nek Pumping lemma a ukažte, že pro jakékoliv čı́slo (mocninu) k jde opět o slovo z daného jazyka. P C Pracujte s těmito jazyky: (a) La = {abi a : i ≥ 0} (b) Lb = {(ab)i : i ≥ 0} (c) Lc = {a(bb)i : i ≥ 1} (d) Ld = {000(111)i : i ≥ 0} (e) Lc = {disk, data, plat} (f) Ld = N (množina přirozených čı́sel, zde včetně 0) Obvyklejšı́m způsobem použitı́ Pumping lemma je jejı́ obrázená forma: mı́sto tvrzenı́ Regular(L) ⇒ Vlastnost(L, p) dokazujeme ¬Vlastnost(L, p) ⇒ ¬Regular(L), tedy „Pokud neexistuje žádné p takové, že pro každé slovo delšı́ než p dokážeme najı́t dané rozdělenı́, pak jazyk nenı́ regulárnı́.“. P 2.1 PUMPING LEMMA PRO REGULÁRNÍ JAZYKY 13 Abychom mohli dokázat, že pracujeme s opravdu jakkoliv dlouhým slovem, použijeme v určenı́ slova „proměnnou“ v exponentu, kterou v přı́padě potřeby můžeme jakkoliv zvyšovat (do nekonečna), napřı́klad proměnnou i ve slově (ab)i . Dále u zvoleného slova zkoušı́me různé možnosti jeho rozdělenı́ na tři části tak, aby prostřednı́ část nebyla prázdné slovo. Nemusı́me hledat absolutně všechny možnosti (to bychom hledali do nekonečna), ale je třeba vystihnout různé možné způsoby umı́stěnı́ hranic mezi potenciálnı́mi částmi. Pro každé možné rozdělenı́ pak najdeme alespoň jednu hodnotu k, pro kterou x · y k · z nepatřı́ do jazyka. Končı́me tehdy, když se podařı́ najı́t slovo takové, že pro jakékoliv jeho rozdělenı́ vždy najdeme alespoň jednu hodnotu k takovou, že x · y k · z nepatřı́ do daného jazyka. Přı́klad 2.2 Ukážeme, že jazyk L = {an b2n : n ≥ 0} nenı́ regulárnı́. Vybereme „dostatečně dlouhé“ slovo jazyka. Protože nemáme zkonstruován konečný automat pro tento jazyk (samozřejmě, vždyt’ ve skutečnosti neexistuje), musı́me vybrat takové slovo, o kterém si můžeme být jisti, že je opravdu dlouhé. Napřı́klad w = ai b2i pro „nějaké“ i, dostatečně velké. Použili jsme proměnnou i, o které vı́me jen jedinou konkrétnı́ věc – je většı́ než 0 (protože musı́ být většı́ než nějaké čı́slo p, které je většı́ než 0). Slovo máme, a to dostatečně reprezentativnı́, vlastně nám určuje všechna dlouhá slova daného jazyka. Zbývá projı́t všechna možná rozdělenı́ x · y · z tohoto slova a dokázat, že pro jakékoliv k ≥ 0 slovo x · y k · z nepatřı́ do jazyka L. U jednotlivých možnostı́ budeme volit k = 0 nebo k = 2. Jsou tyto možnosti:1 1. hranice mezi x a y je před začátkem slova (tj. x = ε): (a) hranice mezi y a z je někde mezi znaky a, v prvnı́ části slova: x · y · z = ε · am · ai−m b2i , m > 0, x · y k · z = akm+i−m b2i , napřı́klad pro k = 0 vycházı́ ai−m b2i ; protože m > 0, toto slovo nepatřı́ do jazyka L (b) hranice mezi y a z je na hranici mezi znaky a a b: x·y ·z = ε·ai ·b2i , x·y k ·z = aki b2i ; opět stačı́ dosadit k = 0, dostáváme b2i , protože i je velké čı́slo, určitě většı́ než 0, toto slovo nenı́ z jazyka L (c) hranice mezi y a z je někde mezi znaky b, v druhé části slova: x · y · z = ε · ai bm · b2i−m , 0 < m < 2i, x · y k · z = (ai bm )k b2i−m , pro k = 0 je taktéž narušen vztah v exponentech – slovo b2i−m ∈ /L (d) hranice mezi y a z je na konci slova, tj. celé slovo je v prostřednı́ části rozdělenı́: x · y · z = ε · ai b2i · ε, x · y 2 · z = ai b2i ai b2i , i > 0 ∈ /L 2. hranice mezi x a y je v prvnı́ části slova, někde mezi znaky a: 1 Poznámka: v rozdělenı́ch se sice v prostřednı́ části vyskytuje index m, přestože jsme si na začátku sekce řekli, že tam žádný do nekonečna rostoucı́ index být nesmı́, zde nám však m zastupuje konečné čı́slo (napřı́klad čı́slo 1, 2, 3, atd.) a jeho účelem je zjednodušit zápis. 2.1 PUMPING LEMMA PRO REGULÁRNÍ JAZYKY 14 (a) hranice mezi y a z je ve stejné části slova: x · y · z = am · an · ai−m−n b2i , m, n > 0, x · y 0 · z = ai−n b2i ∈ /L (b) hranice mezi y a z je na hranici znaků a a b: x · y · y = am · ai−m · b2i , 0 < m < i, x · y 0 · z = am b2i ∈ /L (c) hranice mezi y a z je někde mezi znaky b: x · y · z = am · ai−m bn · b2i−n , m, n > 0, m < i, x · y 2 · z = ai bn ai−m b2i ∈ /L (d) hranice mezi y a z je na konci slova: x · y · z = am · ai−m b2i · ε, 0 < m < i, x · y 0 · z = am ∈ /L 3. hranice mezi x a y je na hranici mezi znaky a a b: (a) hranice mezi y a z je někde mezi znaky b: x · y · z = ai · bm · b2i−m , 0 < m < 2i, x · y 0 · z = ai b2i−m ∈ /L (b) hranice mezi y a z je na konci slova: x · y · z = ai · b2i · ε, x · y 0 · z = ai ∈ /L 4. hranice mezi x a y je v druhé části slova, někde mezi znaky b: (a) hranice mezi y a z je někde mezi znaky b: x·y·z = ai bm ·bn ·b2i−m−n , 0 < m, n < 2i, x · y 0 · z = ai b2i−n ∈ /L (b) hranice mezi y a z je na konci slova: x · y · z = ai bm · b2i−m · ε, 0 < m < 2i, x · y 0 · z = ai bm ∈ /L Jednotlivé varianty rozdělenı́ jsou přehlednějšı́ v tabulce: x·y·z ε · am · ai−m b2i ε · ai · b2i ε · ai bm · b2i−m ε · ai b2i · ε Podmı́nky 0<m<i 0 < m < 2i x · yk · z k=0 akm+i−m b2i aki b2i (ai bm )k b2i−m (ai b2i )k ai−m b2i ∈ /L b2i ∈ /L b2i−m ∈ /L am · an · ai−m−n b2i am · ai−m · b2i am · ai−m bn · b2i−n am · ai−m b2i · ε m, n > 0 0<m<i m, n > 0, m < i 0<m<i akn+i−n b2i am+k(i−m) b2i am (ai−m bn )k b2i−n am (ai−m b2i )k ai−n b2i ∈ /L m 2i a b ∈ /L 0 < m < 2i ai bkm+2i−m ai b2ki ai bm · bn · b2i−m−n ai bm · b2i−m · ε 0 < m, n < 2i 0 < m < 2i ai bkn+2i−n ai bm+k(2i−m) ai b2i−m ∈ /L i a ∈ /L ai · bm · b2i−m ai · b2i · ε am ∈ /L k=2 ai b2i ai b2i ∈ /L ai bn ai−m b2i ∈ /L ai b2i−n ∈ /L i m ab ∈ /L Pro každé možné rozdělenı́ jsme našli k takové, že xy k z nepatřı́ do jazyka, proto L nenı́ regulárnı́ jazyk. 2.1 PUMPING LEMMA PRO REGULÁRNÍ JAZYKY 15 Na testu stačı́ trochu jednoduššı́ postup, jak si ukážeme na následujı́cı́m přı́kladu. Přı́klad 2.3 Použijeme tentýž jazyk jako v předchozı́m přı́kladu, L = {an b2n : n ≥ 0}, a důkaz, že nenı́ regulárnı́, trochu zjednodušı́me. Vı́me, že pokud chceme dokázat, že tento jazyk nenı́ regulárnı́, stačı́, když najdeme dostatečně dlouhé slovo a ukážeme, že toto slovo nelze rozdělit na tři části při splněnı́ podmı́nek stanovených v Pumping lemma. Takže stačı́ určit pozici prostřednı́ části ve slově w = ai b2i , při dělenı́ w = x · y · z: 1. y = ar, r > 0: pak wk = x · y k · z = ai−r+k·r b2i , napřı́klad pro k = 2 by platilo w2 = ai+r b2i pro r > 0, w2 ∈ / L (podobně pro k = 0 a všechny dalšı́ indexy kromě 1, sle stačı́ to ukázat pro jediný index). 2. y = br, r > 0: pak wk = x · y k · z = ai b2i−r+k·r , napřı́klad pro k = 0 by platilo w0 = ai b2i−r , w0 ∈ / L. 3. y = ar bs, r, s > 0: pak wk = x·y k ·z = ai−r (ar bs )k b2i−s , ale pro k > 1 by se v prostřednı́ části střı́daly symboly a a b, což neodpovı́dá předpisu jazyka, proto wk ∈ / L. Jiné rozdělenı́ nenı́ možné, proto jazyk L nenı́ regulárnı́. Přı́klad 2.4 Dokážeme, že jazyk L = {an : n je prvočı́slo} nenı́ regulárnı́. Kdyby tento jazyk byl regulárnı́, pak by pro každé dostatečně dlouhé slovo jazyka existovalo rozdělenı́ s výše uvedenými vlastnostmi. Zvolme slovo w = ai , kde i je některé dostatečně velké prvočı́slo. Nynı́ určı́me rozdělenı́ pro w = x · y · z (v tomto přı́padě nenı́ moc možnostı́, jak volit). Zvolme y = ar pro nějaké r > 1. Pro pumpovánı́ určı́me index k = i + 1 . To můžeme, protože u regulárnı́ho jazyka by věta platila pro jakoukoliv hodnotu indexu k, a čı́slo i je z našeho pohledu konstantnı́ (a dostatečně velké). Určı́me tedy wk = ai−r+k·r , konkrétně wi+1 = ai−r+r(i+1) = ai−r+r·i+r = ai·(1+r) . Exponent lze rozložit na součin dvou čı́sel, která jsou obě většı́ než 2, proto se nejedná o prvočı́slo a toto slovo nepatřı́ do jazyka L. Z toho vyplývá, že L nenı́ regulárnı́. Pumping lemma nenı́ ekvivalence, ale pouze implikace. Proto mohou existovat jazyky, které sice nejsou regulárnı́, ale přesto danou vlastnost splňujı́. Může se jednat napřı́klad o jazyk vzniklý zřetěnenı́m regulárnı́ho a neregulárnı́ho jazyka. P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 16 Přı́klad 2.5 Jazyk L = {ai bj c2j : i, j ≥ 0} nenı́ regulárnı́, přesto splňuje Pumping lemma. Můžeme zvolit napřı́klad slovo am . Toto slovo patřı́ do jazyka L a existuje dokonce vı́ce různých rozdělenı́ x · y · z takových, že x · y k · z ∈ L, napřı́klad ε · (am )k · ε = akm ∈ L pro jakékoliv k ≥ 0. Co z toho plyne? Když jazyk splňuje Pumping lemma, nemůžeme tvrdit, že je regulárnı́. A naopak – když se nepodařı́ pomocı́ Pumping lemma dokázat, že jazyk nenı́ regulárnı́, tak to ještě neznamená, že je regulárnı́. Pumping lemma je jedno z kritériı́ regularity (tj. způsobů, jak lze dokázat, že daný jazyk nenı́ regulárnı́). Úkol 2.2 1. Ukažte, že jazyk L = {ba(ab)n : n ≥ 0} splňuje Pumping lemma. 2. Pomocı́ Pumping lemma dokažte, že jazyk L = {an bcn : n ≥ 1} nenı́ regulárnı́. L C 3. Pomocı́ Pumping lemma dokažte, že jazyk L = {wwR : w ∈ {a, b}∗ } nenı́ regulárnı́. Můžete zvolit napřı́klad slovo v = ai b2i ai , které také patřı́ do jazyka L, stačı́ dokázat, že pro toto slovo neexistuje vhodné rozdělenı́ pro pumpovánı́. 2.2 Uzávěrové vlastnosti – operace nad regulárnı́mi jazyky Třı́da jazyků je množina všech jazyků s danou společnou vlastnostı́. Také regulárnı́ jazyky tvořı́ třı́du – třı́da regulárnı́ch jazyků je množina všech jazyků, pro které lze sestrojit konečný automat. P Vı́me, že na řetězce lze použı́t operaci zřetězenı́. Jazyk je množina řetězců, a tedy na jazyk můžeme použı́t různé množinové operace (sjednocenı́, průnik, doplněk – zrcadlenı́, substituci), a také operace odvozené z práce s řetězci a znaky (signály) – zřetězenı́, dále iteraci (operace „hvězdička“) a dalšı́. Třı́da jazyků je uzavřena vzhledem k nějaké operaci, pokud po uplatněnı́ operace na jazyky z této třı́dy vždy vznikne jazyk patřı́cı́ do stejné třı́dy. Postupně si ukážeme všechny základnı́ operace, a to na konečných automatech. Pokud jde o binárnı́ operaci (uplatňuje se na dva jazyky), je obvykou podmı́nkou disjunktnost průniku množin stavů automatů (žádný stav existuje v obou automatech zároveň). 2.2.1 Sjednocenı́ V přı́padě sjednocenı́ využijeme celou definici původnı́ch automatů. Přidáme nový stav a z tohoto stavu povedeme přechody do všech stavů, do kterých vede přechod z počátečnı́ho stavu. Nový stav nám tedy simuluje použitı́ počátečnı́ch stavů v původnı́ch automatech. P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 17 Postup si můžeme představit třeba tak, že vezmeme všechny přechody, které vedou z původnı́ch počátečnı́ch stavů, a zkopı́rujeme (ne přeneseme, ty původnı́ musejı́ zůstat) jejich začátky (tj. u stavů, ze kterých vycházejı́) k novému stavu. Pokud některý z původnı́ch počátečnı́ch stavů patřı́ i do množiny koncových stavů, pak také tuto vlastnost přidáme novému počátečnı́mu stavu. Označme • A1 = (Q1 , Σ1 , δ1 , q1 , F1 ) je prvnı́ automat, • A2 = (Q2 , Σ2 , δ2 , q2 , F2 ) je druhý automat, pak automat rozpoznávajı́cı́ jazyk, který je sjednocenı́m jazyků obou automatů, je A = (Q1 ∪ Q2 ∪ {s0 }, Σ1 ∪ Σ2 , δ, s0 , F1 ∪ F2 ), platı́, že s0 ∈ / Q1 ∪ Q2 , přechodová funkce: δ(s0 , x) =(δ1 (q1 , x) ∪ δ2 (q2 , x), x ∈ Σ1 ∪ Σ2 δ1 (r, x) : r ∈ Q1 , x ∈ Σ1 , δ(r, x) = δ2 (r, x) : r ∈ Q2 , x ∈ Σ2 To znamená, že přechodovou funkci prakticky přejmeme z původnı́ch automatů, změna bude jen na začátku. Přı́klad 2.6 Ukážeme si operaci sjednocenı́ na následujı́cı́ch jazycı́ch a automatech: L1 = {(ba)i (a ∪ bb) : i ≥ 0} L2 = {ai baaj : i, j ≥ 0} Konečné automaty pro tyto jazyky jsou následujı́cı́: a q1 q0 ↔ q0 b b ← q1 a q2 q2 a b q1 q2 q0 q1 A1 = ({q0 , q1 , q2 }, {a, b}, δ1 , q0 , {q0 , q1 }) δ1 (q0 , a) = q1 δ1 (q0 , b) = q2 δ1 (q2 , a) = q0 δ1 (q2 , b) = q1 b A a B →A a B ←C C a a b A C C B A2 = ({A, B, C}, {a, b}, δ2 , A, {C}) δ2 (A, a) = A δ2 (A, b) = B δ2 (B, a) = C δ2 (C, a) = C Přidáme nový stav s0 a všechny přechody z tohoto stavu nasměrujeme a označı́me stejně jako přechody z počátečnı́ch stavů původnı́ch automatů. Postup je nejnázornějšı́ na stavovém diagramu, ale je jednoduchý také v tabulce přechodů – stačı́ obě tabulky shrnout do jedné a přidat nový řádek, jehož buňky budou sjednocenı́m buněk na řádcı́ch původnı́ch počátečnı́ch stavů a na přı́slušných sloupcı́ch. P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 18 Protože jeden z původnı́ch počátečnı́ch stavů (q0 ) patřı́ do množiny koncových stavů, také nový počátečnı́ stav s0 bude zároveň koncovým. a q1 q0 b b a a b q2 s0 a a a b b a C A B a q1 , A q1 ↔ s0 ← q0 ← q1 q2 A B ←C b q2 , B q2 q0 A q1 B C C A = ({s0 , q0 , q1 , q2 , A, B, C}, {a, b}, δ, s0 , {q0 , q1 , C}) δ(s0 , a) = δ1 (q0 , a) ∪ δ2 (A, a) = {q1 , A} δ(s0 , b) = δ1 (q0 , b) ∪ δ2 (A, b) = {q2 , B} δ(q0 , a) = {q1 } δ(q0 , b) = {q2 } δ(q2 , a) = {q0 } δ(q2 , b) = {q1 } δ(A, a) = {A} δ(A, b) = {B} δ(B, a) = {C} δ(C, a) = {C} Je zřejmé, že L(A) = L(A1 ) ∪ L(A2 ). Pokud by žádný z původnı́ch počátečnı́ch stavů nepatřil do množiny koncových stavů, pak ani stav s0 nesmı́me zařadit do množiny koncových stavů! Znamenalo by to, že do výsledného jazyka nemá patřit prázdné slovo. Úkol 2.3 C 1. Použijte operaci sjednocenı́ na následujı́cı́ konečné automaty: 0 A 1 0 1 C B 0 1 1 E D 2. Použijte operaci sjednocenı́ na následujı́cı́ konečné automaty (q3 zároveň koncový!): → q0 ← q1 ← q2 a b q0 , q2 q1 q2 q2 ↔ q3 q4 q5 ← q6 L a b q4 q6 q5 c q3 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 19 3. Pro oba konečné automaty ze zadánı́ předchozı́ho přı́kladu a také pro výsledný automat vytvořte zbylé dva typy reprezentacı́ – stavový diagram a δ-funkci včetně plné specifikace automatu. 4. Zamyslete se nad tı́m, jak by vypadalo sjednocenı́ vı́ce než dvou konečných automatů. 2.2.2 Zřetězenı́ Zřetězenı́ dvou jazyků vytvořı́me tak, že ve výsledném jazyce jsou všechny možné řetězce, jejichž prvnı́ část je kterékoliv slovo z prvnı́ho jazyka a druhá část zase kterékoliv slovo z druhého jazyka. Záležı́ na pořadı́, části slova nemůžeme přehodit, řı́dı́ se pořadı́m zřetězovaných jazyků. P Přı́klad 2.7 Princip zřetězenı́ jazyků si ukážeme na těchto konečných jazycı́ch: L1 = {ε, abc, ba} L2 = {ddb, dc} Zřetězenı́m těchto dvou jazyků zı́skáme jazyk L = L1 · L2 : L = {ε · ddb, ε · dc, abc · ddb, abc · dc, ba · ddb, ba · dc} = {ddb, dc, abcccb, abcdc, baddb, badc} Při zřetězenı́ nenı́ třeba ve výsledném automatu vytvářet nový stav. Opět využijeme všechny stavy a přechody z původnı́ch automatů a budeme přidávat nové přechody, které je propojı́. Při ukončenı́ výpočtu prvnı́ části slova (tj. v prvnı́m původnı́m automatu) je třeba „plynule“ navázat na výpočet druhého původnı́ho automatu. Proto nově přidávané přechody budou vést z koncových stavů prvnı́ho automatu, jejich cı́lový stav (stav, do kterého bude směřovat šipka přechodu) a označenı́ přejmeme (zkopı́rujeme) od přechodů z počátečnı́ho stavu druhého původnı́ho automatu. Počátečnı́m stavem výsledného automatu bude samozřejmě počátečnı́ stav prvnı́ho původnı́ho automatu. Do množiny koncových stavů zařadı́me • koncové stavy druhého původnı́ho automatu, • pokud počátečnı́ stav druhého automatu byl zároveň koncovým (to znamená, že do druhého jazyka patřilo slovo ε), pak do množiny koncových stavů výsledného automatu zařadı́me také koncové stavy prvnı́ho původnı́ho automatu. Označme • A1 = (Q1 , Σ1 , δ1 , q1 , F1 ) je prvnı́ automat, • A2 = (Q2 , Σ2 , δ2 , q2 , F2 ) je druhý automat, zde je již důležité jejich pořadı́, P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 20 pak automat rozpoznávajı́cı́ jazyk, který je zřetězenı́m jazyků obou automatů, je A = (Q1 ∪ Q2 , Σ1 ∪ Σ2 , δ, q1 , F ), koncové stavy: • F = F2 , pokud ε ∈ / L(A2 ) • F = F1 ∪ F2 , pokud ε ∈ L(A2 ) (tj. jestliže v jazyce druhého automatu bylo prázdné slovo, tedy počátečnı́ stav patřil do množiny koncových stavů, znamená to, že do výsledného jazyka budou patřit i všechna slova rozpoznávaná prvnı́m automatem – zřetězená s ε, lze skončit také ve stavech z F1 ) Přechodová funkce: δ1 (r, x) : r ∈ Q1 − F1 , x ∈ Σ1 , δ(r, x) = δ1 (r, x) ∪ δ2 (q2 , x) : r ∈ F1 , x ∈ Σ1 ∪ Σ2 , δ2 (r, x) : r ∈ Q2 , x ∈ Σ2 Znamená to, že v koncových stavech prvnı́ho původnı́ho automatu budeme (kromě existujı́cı́ch původnı́ch reakcı́) reagovat stejně, jako v počátečnı́m stavu druhého původnı́ho automatu (což je podle našeho značenı́ q2 ). Přı́klad 2.8 Zřetězı́me jazyky těchto konečných automatů: a A1 q1 q0 ↔ q0 b b ← q1 a q2 q2 a b q1 q2 q0 q1 A1 = ({q0 , q1 , q2 }, {a, b}, δ1 , q0 , {q0 , q1 }) δ1 (q0 , a) = q1 δ1 (q0 , b) = q2 δ1 (q2 , a) = q0 δ1 (q2 , b) = q1 b A a B A2 →A a B ←C C a a b A C C B A2 = ({A, B, C}, {a, b}, δ2 , A, {C}) δ2 (A, a) = A δ2 (A, b) = B δ2 (B, a) = C δ2 (C, a) = C V automatu A1 (prvnı́m) jsou dva koncové stavy. Z nich budou vést nové přechody ke stavům automatu A2 – ke všem stavům, do kterých vede přechod z počátečnı́ho stavu A. b a a q0 b a q1 b b a q2 b A a B a C a → q0 q1 q2 A B ←C a b q1 , A A q0 A C C q2 , B B q1 B 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 21 A = ({q0 , q1 , q2 , A, B, C}, {a, b}, δ, {C}) δ(q0 , a) = {q1 , A} δ(q0 , b) = {q2 , B} δ(q1 , a) = {A} δ(q1 , b) = {B} δ(q2 , a) = {q0 } δ(q2 , b) = {q1 } δ(A, a) = {A} δ(A, b) = {B} δ(B, a) = {C} δ(C, a) = {C} Platı́ L(A) = L(A1 ) · L(A2 ). Přı́klad 2.9 Nynı́ použijeme stejné zadánı́ jako v předchozı́m přı́kladu, jen obrátı́me pořadı́ zřetězovaných automatů. Musı́me brát v úvahu to, že počátečnı́ stav automatu, který je ted’ druhý v pořadı́, je zároveň koncovým stavem, což bude mı́t vliv také na množinu koncových stavů: b A a B a a C a b a q1 q0 b b a q2 →A B ←C q0 q1 q2 a b A C C, q1 q1 B q0 q2 q2 q1 A0 = ({q0 , q1 , q2 , A, B, C}, {a, b}, δ, {C, q0 , q1 }) δ(A, a) = {A} δ(A, b) = {B} δ(B, a) = {C} δ(C, a) = {C, q1 } δ(C, b) = {q2 } δ(q0 , a) = {q1 } δ(q0 , b) = {q2 } δ(q2 , a) = {q0 } δ(q2 , b) = {q1 } Platı́ L(A0 ) = L(A2 ) · L(A1 ). Úkol 2.4 1. Proved’te operaci zřetězenı́ jazyků následujı́cı́ch konečných automatů: 0 A 1 0 1 C B 0 1 1 E D C 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 22 2. Proved’te operaci zřetězenı́ jazyků následujı́cı́ch konečných automatů – nejdřı́v v pořadı́ podle zadánı́ (L(A1 ) · L(A2 )) a potom v opačném pořadı́ (L(A2 ) · L(A1 )). Sestrojte také stavové diagramy. A1 → q0 ← q1 ← q2 2.2.3 a b q0 , q2 q1 q2 A2 ↔ q3 q4 q5 ← q6 q2 a b q4 q6 q5 c q3 Iterace (Kleeneho uzávěr) Při iteraci zřetězujeme jazyk sám se sebou, a to 0×, 1×, 2×, . . . , a pak všechny výsledné jazyky po zřetězenı́ sjednotı́me. Formálně to můžeme zapsat následovně: L∗ = ∞ [ P Li i=0 kde Li je i-násobné zřetězenı́ jazyka L sama se sebou (napřı́klad pro i = 3 bylo L3 = L·L·L). Zřetězenı́ jsme již probı́rali. Přı́klad 2.10 Princip iterace jazyka si ukážeme na tomto konečném jazyce: L1 = {abc, ba, cd} Iteracı́ zı́skáme jazyk L = L∗1 : L = { ε, ................................................................. abc, ba, cd, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . abcabc, abcba, abccd, baabc, baba, bacd, .............................. abcabcabc, abcabcba, abcabccd, abcbaabc, abcbaba, abcbacd, . . .} ....... 0× 1× 2× 3×, . . . Výsledkem iterace je vždy nekonečný jazyk obsahujı́cı́ prázdné slovo ε, i kdyby původnı́ jazyk L1 byl konečný a i kdyby prázdné slovo neobsahoval. Úprava konečného automatu při iteraci spočı́vá v těchto dvou krocı́ch: 1. vytvořı́me nový počátečnı́ stav, přechody z něj vedoucı́ určı́me stejné jako ty, které vedou z původnı́ho počátečnı́ho stavu, 2. vytvořı́me nové přechody umožňujı́cı́ „návrat“ z koncových stavů na počátek výpočtu (dalšı́ slovo z jazyka), L P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 23 3. nový počátečnı́ stav zařadı́me do množiny koncových stavů (tı́m zařadı́me do jazyka slovo ε). Prvnı́ bod je nevyhnutelný předevšı́m tehdy, pokud v původnı́m automatu vede některý přechod do počátečnı́ho stavu a zároveň tento stav nebyl v množině koncových stavů; je třeba zabránit tomu, aby zařazenı́ počátečnı́ho stavu do množiny koncových stavů mělo za následek rozpoznávánı́ takových slov, která do jazyka správně patřit nemajı́. Označme A1 = (Q1 , Σ, δ1 , q0 , F1 ) původnı́ automat, pak automat rozpoznávajı́cı́ jazyk, který je iteracı́ původnı́ho jazyka, je A = (Q1 ∪ {s0 }, Σ, δ, s0 , F1 ∪ {s0 }), přechodová funkce: δ(r, x) = δ1 (r, x) : r ∈ Q1 − F1 , x ∈ Σ, δ1 (r, x) ∪ δ1 (q0 , x) : r ∈ F1 , x ∈ Σ, δ1 (q0 , x) : r = s0 , x ∈ Σ Nové přechody povedou z původnı́ch koncových stavů, a to do všech stavů, do kterých vede přechod z počátečnı́ho stavu. Princip je prakticky stejný jako u dřı́ve probı́raných operacı́, kopı́rujeme počátky přechodů od počátečnı́ho stavu se zachovánı́m jejich cı́le i označenı́ (signálu). Přı́klad 2.11 Vytvořı́me iteraci jazyka následujı́cı́ho automatu: A1 = ({A, B, C, D}, {0, 1, 2}, δ1 , A, {B, D}) 0 1 A 2 0 C 2 →A ←B C ←D 1 B D 0 1 2 A B D C B δ1 (A, 0) = A δ1 (A, 1) = B δ1 (A, 2) = C δ1 (B, 1) = D δ1 (B, 2) = B δ1 (C, 0) = D D Koncové stavy jsou dva. Z každého přidáme přechody do všech stavů, do kterých směřujı́ přechody z počátečnı́ho stavu, a potom počátečnı́ stav označı́me jako koncový (aby automat přijı́mal také prázdné slovo ε). 1 0 s0 2 0 0 1 A 2 2 0 C 2 1, 2 1 1 B D 0 0 ↔ s0 A ←B C ←D A A A D A 1 2 B B D, B C C B, C B C P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 24 A = ({s0 , A, B, C, D}, {0, 1, 2}, δ, s0 , {s0 , B, D}) δ(s0 , 0) = A δ(s0 , 1) = B δ(s0 , 2) = C δ(A, 0) = A δ(A, 1) = B δ(A, 2) = C δ(B, 1) = D δ(B, 2) = B δ(C, 0) = D δ(B, 0) = A δ(B, 1) = B δ(B, 2) = C δ(D, 0) = A δ(D, 1) = B δ(D, 2) = C Kdyby stav A už v původnı́m automatu patřil do množiny koncových stavů, tak by samozřejmě koncovým stavem zůstal. Úkol 2.5 1. Zkonstruujte konečný automat jazyka, který je iteracı́ jazyka následujı́cı́ho automatu: a q1 q0 b b a q2 A1 ↔ q0 ← q1 q2 a b q1 q2 q0 q1 C 2. Zkonstruujte konečný automat jazyka, který je iteracı́ jazyka následujı́cı́ho automatu: 0 1 1 E D →D ←E 0 1 D E E Pro výsledný automat také vytvořte třetı́ typ reprezentace – δ-funkci s plnou specifikacı́. 2.2.4 Pozitivnı́ iterace Pozitivnı́ iterace je podobná operace jako předchozı́, ale zatı́mco iterace znamená řetězenı́ 0×, 1×, 2×, . . ., při pozitivnı́ iteraci začı́náme při řetězenı́ až 1×, 2×, . . .. Matematicky můžeme zapsat iteraci a pozitivnı́ iteraci následovně: L∗ = ∞ [ i=0 Li L+ = ∞ [ i=1 Li P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 25 Postup je stejný jako u iterace, ale pokud počátečnı́ stav původnı́ho automatu nepatřil do množiny koncových stavů (tj. slovo ε nepatřilo do jazyka), nebude koncovým stavem ani po úpravě automatu. Označme A1 = (Q1 , Σ, δ1 , q0 , F1 ) původnı́ automat, pak automat rozpoznávajı́cı́ jazyk, který je pozitivnı́ iteracı́ původnı́ho jazyka, je A = (Q1 ∪ {s0 }, Σ, δ, s0 , F ), kde F = F1 ∪ {s0 }, pokud q0 ∈ F1 , jinak F = F1 . Přechodová funkce: δ1 (r, x) : r ∈ Q1 − F1 , x ∈ Σ, δ(r, x) = δ1 (r, x) ∪ δ1 (q0 , x) : r ∈ F1 , x ∈ Σ, δ1 (q0 , x) : r = s0 , x ∈ Σ Přı́klad 2.12 Vytvořı́me pozitivnı́ iteraci jazyka následujı́cı́ho automatu: A1 = ({A, B, C, D}, {0, 1, 2}, δ1 , A, {B, D}) 0 1 A 2 0 C 2 →A ←B C ←D 1 B D 0 1 2 A B D C B δ1 (A, 0) = A δ1 (A, 1) = B δ1 (A, 2) = C δ1 (B, 1) = D δ1 (B, 2) = B δ1 (C, 0) = D D Přidáváme pouze nové přechody, počátečnı́ stav nebudeme zařazovat do množiny koncových stavů, protože v původnı́m automatu také nebylo rozpoznáváno slovo ε: 1 0 s0 2 0 0 1 A 2 2 0 C 2 1, 2 1 1 B D 0 → s0 A ←B C ←D A A A D A 1 2 B B D, B C C B, C B C 0 A = ({s0 , A, B, C, D}, {0, 1, 2}, δ, s0 , {B, D}) δ(s0 , 0) = A δ(s0 , 1) = B δ(s0 , 2) = C δ(A, 0) = A δ(A, 1) = B δ(A, 2) = C δ(B, 1) = D δ(B, 2) = B δ(C, 0) = D δ(B, 0) = A δ(B, 1) = B δ(B, 2) = C δ(D, 0) = A δ(D, 1) = B δ(D, 2) = C P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 26 Pokud původnı́ jazyk L obsahuje slovo ε, pak platı́ L∗ = L+ , v opačném přı́padě se tyto jazyky nerovnajı́ a platı́ L∗ = L+ ∪ {ε}. Úkol 2.6 1. Zkonstruujte konečný automat jazyka, který je pozitivnı́ iteracı́ jazyka následujı́cı́ho automatu: a q1 q0 b b a q2 A1 ↔ q0 ← q1 q2 a b q1 q2 q0 q1 L C 2. Zkonstruujte konečný automat jazyka, který je pozitivnı́ iteracı́ jazyka následujı́cı́ho automatu: 0 1 1 E D 2.2.5 →D ←E 0 1 D E E Zrcadlový obraz (reverze) Reverze je převrácenı́. Zrcadlový obraz slova sestrojı́me tak, že je zrcadlově „převrátı́me“, tj. napřı́klad ze slova abcd zı́skáme slovo (abcd)R = dcba. Zrcadlový obraz jazyka je jazyk obsahujı́cı́ všechna slova původnı́ho jazyka, ale převrácená. Operace zrcadlenı́ je sama k sobě inverznı́ – když slovo (nebo jazyk) revertujeme dvakrát, dostáváme původnı́ slovo (jazyk). Zrcadlenı́ můžeme zapsat takto: LR = {w : wR ∈ L} Pokud budeme při reverzi pracovat s konečným automatem, předevšı́m převrátı́me všechny cesty, které v automatu existujı́ (tj. převrátı́me všechny přechody) a zaměnı́me počátečnı́ a koncové stavy. Dále musı́me vyřešit jeden problém – počátečnı́ stav musı́ být vždy jen jeden, ale koncových stavů může být obecně jakýkoliv počet. Proto rozlišı́me dva přı́pady – pokud původnı́ automat má jen jediný koncový stav, stačı́ postup naznačený v předchozı́m odstavci. Jestliže však má vı́ce koncových stavů, musı́me zajistit, aby po reverzi existoval jen jediný počátečnı́ stav. Proto vytvořı́me nový stav, který v sobě bude shrnovat vlastnosti původnı́ch P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 27 koncových stavů (resp. nových „počátečnı́ch“ stavů) týkajı́cı́ se přechodů, které z nich po reverzi vycházejı́. Pokud počátečnı́ stav původnı́ho automatu patřil do množiny koncových stavů, bude koncovým stavem také počátečnı́ stav po reverzi. Označme A1 = (Q1 , Σ, δ1 , q0 , F1 ) původnı́ automat, pak automat rozpoznávajı́cı́ jazyk, který je reverzı́ původnı́ho jazyka, je A = (Q1 ∪ {s0 }, Σ, δ, s0 , F ), množina koncových stavů je F = {q0 , s0 }, pokud q0 ∈ F1 , jinak F = {q0 } (záležı́ na tom, zda do původnı́ho jazyka patřilo ε). Přechodová funkce: ( δ(r, x) = p : δ1 (p, x) = r, x ∈ Σ, t : r = s0 , δ1 (t, x) ∈ F1 , x ∈ Σ Na přechodové funkci vidı́me, že podle prvnı́ho řádku předpisu se všechny přechody obrátı́ (tj. zaměnı́ se zdroj a cı́l přechodu), podle druhého řádku vytvořı́me přechody vedoucı́ z nového (počátečnı́ho) stavu s0 . Přı́klad 2.13 Provedeme operaci zrcadlenı́ jazyka tohoto konečného automatu: A1 = ({q0 , q1 , q2 , q3 }, {a, b, c}, δ1 , q0 , {q1 }) a q0 b q1 c c b q2 a → q0 ← q1 q2 q3 a b q1 q2 c δ1 (q0 , a) = q1 δ1 (q0 , b) = q2 δ1 (q1 , c) = q1 δ1 (q2 , a) = q3 δ1 (q2 , c) = q1 δ1 (q3 , b) = q2 q1 q1 q3 q2 q3 Obrátı́me všechny přechody. Protože máme jen jediný koncový stav, stačı́ pak jen zaměnit počátečnı́ a koncový stav (nebudeme vytvářet nový stav s0 ). U tabulky se zaměňujı́ označenı́ řádků s obsahem buněk na tomto řádku. AR = ({q0 , q1 , q2 , q3 }, {a, b, c}, δ, q1 , {q0 }) q1 c a q0 c b b q2 a a ← q0 → q1 q2 q3 q3 b q0 q1 , q2 q0 , q3 q2 c δ1 (q1 , a) = q0 δ1 (q2 , b) = q0 δ1 (q1 , c) = q1 δ1 (q3 , a) = q2 δ1 (q1 , c) = q2 δ1 (q2 , b) = q3 P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 28 Přı́klad 2.14 Podı́váme se na složitějšı́ přı́pad – konečný automat s vı́ce koncovými stavy. Postup bude podobný, ale navı́c řešı́me nutnost existence jediného počátečnı́ho stavu. Je dán tento konečný automat: b A b a C b B →A ←B C ←D c a D a b C B c D A, D D Jsou zde dva koncové stavy. Nejdřı́v přesměrujeme všechny přechody a označı́me nový koncový stav, a až v druhém kroku (automat vpravo) vytvořı́me nový počátečnı́ stav podobným postupem, jaký jsme použili napřı́klad u sjednocenı́ (tam šlo také o „simulaci“ – nahrazenı́ dvou počátečnı́ch stavů jediným). b A b a C b B ⇒ c D a b A b a C b b c s0 B c a D a b → s0 ←A B C D a b c D A, C C A B C B A D Poslednı́ přı́pad, na který se podı́váme, je konečný automat s vı́ce koncovými stavy, kde také počátečnı́ stav patřı́ do množiny koncových stavů. Přı́klad 2.15 Pro operaci zrcadlenı́ je dán tento konečný automat: A1 = ({q0 , q1 , q2 }, {a, b}, δ1 , q0 , {q0 , q1 }) a q1 q0 b b a q2 ↔ q0 ← q1 q2 a b q1 q2 q0 q1 δ1 (q0 , a) = q1 δ1 (q0 , b) = q2 δ1 (q2 , a) = q0 δ1 (q2 , b) = q1 Narozdı́l od předchozı́ch přı́kladů budou ve výsledném automatu dva koncové stavy. Stav q0 bude koncový, protože v původnı́m automatu je počátečnı́m stavem, a stav s0 bude 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 29 koncový, protože do jazyka původnı́ho automatu patřı́ slovo ε, jehož převrácenı́m je opět slovo ε pro jeho rozpoznánı́ musı́ být počátečnı́ stav (tj. s0 ) v množině koncových stavů. AR = ({s0 , q0 , q1 , q2 }, {a, b}, δ, s0 , {q0 , s0 }) a Úkol a q0 b q1 b a q2 s0 a, b ↔ s0 ← q0 q1 q2 a b q0 , q2 q2 q0 q2 δ(s0 , a) = {q0 , q2 } δ(s0 , b) = {q2 } δ(q1 , a) = {q0 } δ(q2 , b) = {q0 } δ(q0 , a) = {q2 } δ(q1 , b) = {q2 } q2 q0 2.7 1. Zkonstruujte konečný automat jazyka, který je reverzı́ (zrcadlovým obrazem) jazyka následujı́cı́ho automatu: 0 1 1 E D →D ←E 0 1 D E E 2. Vytvořte konečný automat jazyka, který je reverzı́ jazyka následujı́cı́ho automatu. Pro oba automaty (uvedený i ten, který vytvořı́te) sestrojte tabulku přechodů. b a b q1 q0 3. Vytvořte konečný automat rozpoznávajı́cı́ jazyk L = {ε, tisk, tis, sı́to} tak, aby jednotlivá slova jazyka byla rozpoznávaná koncovým stavem (tj. pro každé slovo vlastnı́ koncový stav). Potom proved’te reverzi tohoto automatu. 4. Vytvořte zrcadlový obraz jazyka tohoto konečného automatu (má jediný koncový stav, který je zároveň počátečnı́m stavem): ↔ q0 q1 q2 a b q1 q2 q2 q0 q2 C 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 2.2.6 30 Průnik Když vytvářı́me konečný automat pro průnik dvou jazyků pomocı́ automatů, které je rozpoznávajı́, tak vlastně simulujeme (paralelně) činnost obou těchto automatů. Po přečtenı́ každého signálu ze vstupu provedeme jeden krok v obou automatech zároveň. Ve výsledném automatu jsou proto stavy uspořádanými dvojicemi stavů z prvnı́ho a druhého původnı́ho automatu (pozor, záležı́ na pořadı́). Označme • A1 = (Q1 , Σ1 , δ1 , q1 , F1 ) je prvnı́ automat, • A2 = (Q2 , Σ2 , δ2 , q2 , F2 ) je druhý automat, pak automat rozpoznávajı́cı́ jazyk, který je průnikem jazyků obou automatů, je AP = (Q1 × Q2 , Σ1 ∩ Σ2 , δ, [q1 , q2 ], F1 × F2 ), kde operace × je kartézským součinem uvedených množin. Přechodová funkce: δ([p, r], x) = [δ1 (p, x), δ2 (r, x)], kde p ∈ Q1 , r ∈ Q2 , x ∈ Σ2 , x ∈ Σ1 ∩ Σ2 Přı́klad 2.16 Sestrojı́me konečný automat rozpoznávajı́cı́ průnik jazyků následujı́cı́ch dvou automatů: a b q0 b b q1 a b q2 q3 a p0 a p2 b a p1 b b p3 p4 A1 = ({q0 , . . . , q3 }, {a, b}, δ1 , q0 , {q2 }) A2 = ({p0 , . . . , p4 }, {a, b}, δ2 , p0 , {p4 }) δ1 (q0 , a) = {q0 } δ1 (q0 , b) = {q1 } δ1 (q1 , b) = {q1 , q2 } δ2 (p0 , a) = {p1 , p3 } δ2 (p1 , a) = {p2 } δ2 (p1 , b) = {p1 } δ1 (q2 , a) = {q3 } δ1 (q3 , b) = {q2 } Nejdřı́v si vyznačı́me průběh „simulace“. Nenı́ to nutné (a u složitějšı́ho automatu je to prakticky neproveditelné), ale na diagramu lépe pochopı́me paralelnost obou zpracovánı́ téhož slova (červeně jsou spojeny stavy, ve kterých jsou automaty ve stejném kroku zpracovánı́ slova) – obrázek vpravo. Napřı́klad pro slovo abbabab paralelně procházı́me dvojicemi stavů [q0 , p0 ], pak [q0 , p1 ], [q1 , p1 ], [q2 , p1 ], [q3 , p2 ], [q2 , p0 ], dále [q3 , p3 ] a [q2 , p4 ]. δ2 (p2 , b) = {p0 } δ2 (p3 , b) = {p4 } a b q0 b b q1 a a p0 b a p1 b p2 a b p3 b q2 p4 q3 P P 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 31 Protože by bylo hodně náročné (a také nedeterministické a těžko naprogramovatelné) takto vyhledávat všechny dvojice stavů, ve kterých se nacházı́ výpočet v obou automatech ve stejném kroku, použijeme jinou (trochu „otrockou“) metodu: • jako množinu stavů použijeme množinu všech uspořádaných dvojic, kde prvnı́ prvek je stav prvnı́ho automatu a druhý prvek je stav druhého automatu, • vytvořı́me δ-funkci nebo tabulku přechodů, ve které zachytı́me společné přechody na stejný signál v obou automatech, • odstranı́me nepotřebné stavy. Počátečnı́m stavem bude uspořádaná dvojice obsahujı́cı́ počátečnı́ stavy obou původnı́ch automatů, koncové stavy budou všechny uspořádané dvojice, ve kterých jsou oba původnı́ stavy koncovými. Jednoduššı́ je využitı́ tabulky přechodů. Podle tabulek původnı́ch automatů vytvořı́me tabulku pro výsledný automat (kombinace řádků ve stejném sloupci). → q0 q1 ← q2 q3 a b a q0 q1 q1 , q2 → p0 p1 p2 p3 ← p4 q3 q2 b p1 , p3 p2 p1 p0 p4 Tabulka přechodů výsledného konečného automatu má 4 · 5 = 20 řádků: a → [q0 , p0 ] [q0 , p1 ] [q0 , p2 ] [q0 , p3 ] [q0 , p4 ] [q0 , p1 ], [q0 , p3 ] [q0 , p2 ] [q1 , p0 ] [q1 , p1 ] [q1 , p2 ] [q1 , p3 ] [q1 , p4 ] b [q1 , p1 ] [q1 , p0 ] [q1 , p4 ] [q1 , p1 ], [q2 , p1 ] [q1 , p0 ], [q2 , p0 ] [q1 , p4 ], [q2 , p4 ] (pokračovánı́) [q2 , p0 ] [q2 , p1 ] [q2 , p2 ] [q2 , p3 ] ← [q2 , p4 ] [q3 , p0 ] [q3 , p1 ] [q3 , p2 ] [q3 , p3 ] [q3 , p4 ] a b [q3 , p1 ], [q3 , p3 ] [q3 , p2 ] [q2 , p1 ] [q2 , p0 ] [q2 , p4 ] Nynı́ odstranı́me nepotřebné (tj. nedosažitelné a nadbytečné) stavy tak, jak jsme se učili v předchozı́ch kapitolách, obsah množin je průběžně třı́děn pro usnadněnı́ porovnávánı́. S0 = {[q0 , p0 ]} S1 = {[q0 , p0 ], [q0 , p1 ], [q0 , p3 ]} 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 32 = {[q0 , p0 ], [q0 , p1 ], [q0 , p3 ], [q0 , p2 ], [q1 , p1 ], [q1 , p4 ]} = {[q0 , p0 ], [q0 , p1 ], [q0 , p2 ], [q0 , p3 ], [q1 , p1 ], [q1 , p4 ], [q1 , p0 ], [q2 , p1 ]} = {[q0 , p0 ], [q0 , p1 ], [q0 , p2 ], [q0 , p3 ], [q1 , p0 ], [q1 , p1 ], [q1 , p4 ], [q2 , p1 ], [q3 , p2 ]} = {[q0 , p0 ], [q0 , p1 ], [q0 , p2 ], [q0 , p3 ], [q1 , p0 ], [q1 , p1 ], [q1 , p4 ], [q2 , p1 ], [q3 , p2 ], [q2 , p0 ]} = {[q0 , p0 ], [q0 , p1 ], [q0 , p2 ], [q0 , p3 ], [q1 , p0 ], [q1 , p1 ], [q1 , p4 ], [q2 , p0 ], [q2 , p1 ], [q3 , p2 ], [q3 , p1 ], [q3 , p3 ]} S7 = {[q0 , p0 ], [q0 , p1 ], [q0 , p2 ], [q0 , p3 ], [q1 , p0 ], [q1 , p1 ], [q1 , p4 ], [q2 , p0 ], [q2 , p1 ], [q3 , p1 ], [q3 , p2 ], [q3 , p3 ], [q2 , p4 ]} S8 = {[q0 , p0 ], [q0 , p1 ], [q0 , p2 ], [q0 , p3 ], [q1 , p0 ], [q1 , p1 ], [q1 , p4 ], [q2 , p0 ], [q2 , p1 ], [q2 , p4 ], [q3 , p1 ], [q3 , p2 ], [q3 , p3 ]} = S7 S2 S3 S4 S5 S6 Odstranili jsme stavy [q0 , p4 ], [q1 , p2 ], [q1 , p3 ], [q2 , p2 ], [q2 , p3 ], [q3 , p0 ], [q3 , p4 ], které jsou nedosažitelné z počátečnı́ho stavu. Dále pracujeme s touto tabulkou přechodů: a → [q0 , p0 ] [q0 , p1 ] [q0 , p2 ] [q0 , p3 ] [q1 , p0 ] [q1 , p1 ] [q1 , p4 ] [q2 , p0 ] [q2 , p1 ] ← [q2 , p4 ] [q3 , p1 ] [q3 , p2 ] [q3 , p3 ] [q0 , p1 ], [q0 , p3 ] [q0 , p2 ] b [q1 , p1 ] [q1 , p0 ] [q1 , p4 ] [q1 , p1 ], [q2 , p1 ] [q3 , p1 ], [q3 , p3 ] [q3 , p2 ] [q2 , p1 ] [q2 , p0 ] [q2 , p4 ] Odstranı́me nadbytečné symboly (ze kterých neexistuje cesta do koncového stavu): E0 E1 E2 E3 E4 E5 E6 E7 E8 = {[q2 , p4 ]} = {[q2 , p4 ], [q3 , p3 ]} = {[q2 , p4 ], [q3 , p3 ], [q2 , p0 ]} = {[q2 , p0 ], [q2 , p4 ], [q3 , p3 ], [q3 , p2 ]} = {[q2 , p0 ], [q2 , p1 ], [q2 , p4 ], [q3 , p2 ], [q3 , p3 ]} = {[q2 , p0 ], [q2 , p1 ], [q2 , p4 ], [q3 , p2 ], [q3 , p3 ], [q1 , p1 ], [q3 , p1 ]} = {[q1 , p1 ], [q2 , p0 ], [q2 , p1 ], [q2 , p4 ], [q3 , p1 ], [q3 , p2 ], [q3 , p3 ], [q0 , p1 ]} = {[q0 , p1 ], [q1 , p1 ], [q2 , p0 ], [q2 , p1 ], [q2 , p4 ], [q3 , p1 ], [q3 , p2 ], [q3 , p3 ], [q0 , p0 ]} = {[q0 , p0 ], [q0 , p1 ], [q1 , p1 ], [q2 , p0 ], [q2 , p1 ], [q2 , p4 ], [q3 , p1 ], [q3 , p2 ], [q3 , p3 ]} = E7 Po odstraněnı́ stavů [q0 , p2 ], [q0 , p3 ], [q1 , p0 ], [q1 , p4 ] dostáváme tuto tabulku přechodů (s původnı́m označenı́m stavů a po přeznačenı́ na pı́smena): 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY a → [q0 , p0 ] [q0 , p1 ] [q1 , p1 ] [q2 , p0 ] [q2 , p1 ] ← [q2 , p4 ] [q3 , p1 ] [q3 , p2 ] [q3 , p3 ] b a →A B C D E ←F G H K [q0 , p1 ] [q1 , p1 ] [q1 , p1 ], [q2 , p1 ] [q3 , p1 ], [q3 , p3 ] [q3 , p2 ] [q2 , p1 ] [q2 , p0 ] [q2 , p4 ] Stavový diagram (stavy jsou přeznačené): a A Úkol b B b C b 33 a E b a G b B C C, E G, K H E D F H b a D b K F 2.8 1. Vytvořte deterministické konečné automaty pro jazyky L1 = {if, then, else} a L2 = {if, iff, elif}, v každém z automatů stačı́ jediný koncový stav pro všechna slova daného jazyka. Potom pomocı́ automatů vytvořte průnik těchto jazyků. 2. Sestrojte konečný automat, který je průnikem jazyků následujı́cı́ch automatů (pracujte s tabulkami přechodů). Odstraňte všechny nedosažitelné a nadbytečné stavy a sestrojte stavový diagram. →A ←B C D 0 1 B C A D B →E F ←H 0 1 F H F H H Pro kontrolu: po odstraněnı́ nepotřebných stavů by měl automat mı́t osm stavů, z toho jeden koncový, jeden cyklus přes jeden stav a jeden cyklus přes tři stavy. C 2.2 UZÁVĚROVÉ VLASTNOSTI – OPERACE NAD REGULÁRNÍMI JAZYKY 2.2.7 34 Homomorfismus Homomorfismus je takové zobrazenı́, které • zachovává neutrálnı́ prvek (tj. u řetězců prázdný řetězec ε zobrazı́ opět na ε) • zachovává zobrazenı́ operace, pro kterou je definován (u řetězců jde o zřetězenı́, tj. h(a · b) = h(a) · h(b)) • výsledkem zobrazenı́ prvku (signálu, znaku) je vždy řetězec (u substituce, což je zobecněnı́ homomorfismu, je výsledkem zobrazenı́ množina řetězců). Když konstruujeme konečný automat homomorfismu pro některý jazyk, využı́váme plně způsobu definice daného homomorfismu – pokud je v předpisu tohoto zobrazenı́ napřı́klad h(a) = xyz, tj. znak a má být zobrazen na řetězec xyz, vezmeme postupně všechny přechody označené znakem a a nahradı́me je cestami rozpoznávajı́cı́mi slovo xyz. Každá z těch cest musı́ být samozřejmě samostatná, vždy jeden konkrétnı́ přechod označený signálem a nahradı́me jednou samostatnou cestou pro xyz. Přı́klad 2.17 Podle následujı́cı́ho automatu sestrojı́me konečný automat rozpoznávajı́cı́ jazyk podle zadaného homomorfismu. ↔ q0 q1 ← q2 a b q1 q2 q1 c q2 Homomorfismus: h(a) = df, h(b) = f f d, h(c) = d a q0 b a q1 c q2 Po úpravě vypadá konečný automat následovně: ↔ q0 q1 ← q2 q3 q4 q5 q6 d f q5 q6 q2 q3 q4 q1 q1 q2 Původnı́ abeceda: Σ1 = {a, b, c} Nová abeceda: Σ2 = {d, f } f q4 q3 f d d q0 q2 q1 d f d f q5 q6 Jazyk rozpoznávaný původnı́m automatem je L = {ε} ∪ {abi acj : i, j ≥ 0}, po úpravě dostáváme automat rozpoznávajı́cı́ jazyk h(L) = {ε} ∪ {df (ff d)i df dj : i, j ≥ 0}. 2.3 SESTROJENÍ KONEČNÉHO AUTOMATU PODLE REGULÁRNÍHO VÝRAZU Úkol 35 2.9 C Je dán konečný automat A s jazykem L = L(A) a homomorfismy h1 a h2 . h1 (a) = 01 h1 (b) = 110 h1 (c) = 1 → q0 ← q1 q2 q3 h2 (a) = dd h2 (b) = g h3 (c) = dg a b q1 q2 c q1 q1 q3 q2 a q0 b q1 c c b q2 a q3 Zjistěte jazyky h1 (L) a h2 (L) a vytvořte také konečné automaty pro tyto jazyky (úpravou automatu A). Jazyk automatu A je L(A) = {aci : i ≥ 0} ∪ {b(ab)i ccj : i, j ≥ 0}. Dále pro výsledné automaty sestrojte jejich δ-funkci včetně plné specifikace, nezapomeňte na změnu abecedy. 2.3 Sestrojenı́ konečného automatu podle regulárnı́ho výrazu Při sestrojenı́ konečného automatu podle zadaného regulárnı́ho výrazu využı́váme vztahu mezi prvky regulárnı́ho výrazu (+, ·, ∗ ) a operacemi nad množinami řetězců ∪, ·, ∗ ). V předchozı́ch cvičenı́ch jsme se naučili pracovat s regulárnı́mi operacemi (sjednocenı́, zřetězenı́ a iteraci) a tyto postupy použijeme také zde. Přı́klad 2.18 Sestrojı́me konečný automat pro regulárnı́ výraz a∗ b + (b∗ a + bbc)∗ . Postupně vytvořı́me konečné automaty pro jednotlivé podvýrazy a zároveň je zjednodušı́me. a 0 REG1 = a 1 REG3 = (REG1 )∗ = a∗ a a 1 0 REG5 = ((REG2 )∗ · REG1 ) = b∗ a b a 3 5 b 2 REG2 = b 3 REG4 = (REG3 · REG2 ) = a∗ b a b 1 b 6 REG6 = bbc 4 b 7 c 9 8 P 2.4 VYTVOŘENÍ REGULÁRNÍHO VÝRAZU PODLE KONEČNÉHO AUTOMATU REG7 = (REG5 + REG6 ) = b∗ a + bbc b a 5 3 b a 10 b b @ @6 @ 36 REG8 = (REG7 )∗ = (b∗ a + bbc)∗ b b a a 5 3 b b a b c b 9 8 7 10 b 7 b a c 9 8 b Výsledný automat: REG9 = (REG4 + REG8 ) = a∗ b + (b∗ a + bbc)∗ a b 4 1 a b 11 b b a b b a a 5 3 b b a @ @ 10 @ b b b c 7 8 b a → 11 1 3 ←4 ←5 7 8 ←9 a b 1, 5 1 5 3, 4, 7 4 3 5 3, 7 8 c 9 5 3, 7 9 Stav 10 je nedosažitelný z počátečnı́ho stavu, proto může být odstraněn (v tabulce vpravo se již nenacházı́). Úkol 2.10 Sestrojte konečné automaty podle těchto regulárnı́ch výrazů: • (a + b∗ ) · b∗ • a · (b + (ab)∗ ) + b • (01 + 10)∗ 10∗ • (abc)∗ · a · (ba + c) • a∗ ba · (bb∗ + ca)∗ · a C 2.4 VYTVOŘENÍ REGULÁRNÍHO VÝRAZU PODLE KONEČNÉHO AUTOMATU 2.4 37 Vytvořenı́ regulárnı́ho výrazu podle konečného automatu Pokud máme zjistit jazyk rozpoznávaný zadaným konečným automatem, obvykle vytvářı́me regulárnı́ výraz (který pak můžeme převést do množinové reprezentace). Postupujeme takto: 1. Pokud je to nutné, přeznačı́me stavy (tak, aby byly označeny čı́sly od 1). 2. Jako bázi rekurze (indukce) použijeme přı́mé cesty mezi dvěma stavy, na kterých se 0 a dokážeme je jednoduše nenacházı́ žádný dalšı́ stav (tj. přechody); značı́me je Rij zjistit přı́mo z automatu (v jakékoliv reprezentaci), napřı́klad: ... i j p ... a b i, j p j p j, p a a i b a, b j a b p 0 =a+ε Rii 0 =a Rij 0 =b Rip 0 =∅ Rji 0 =ε Rjj 0 =a Rjp 0 =∅ Rpi 0 =a+b Rpj 0 =b+ε Rpp k – je 3. Indukcı́ budeme postupně cesty prodlužovat (spojovat). Vytvářı́me množiny Rij to regulárnı́ výraz odpovı́dajı́cı́ cestě ze stavu i do stavu j vedoucı́ pouze přes stavy označené maximálně čı́slem k. Postupujeme podle vzorce: k−1 k−1 ∗ k−1 k−1 k + Rik · (Rkk ) · Rkj Rij = Rij Použı́váme výhradně množiny zjištěné v předchozı́m kroku (k − 1). 4. Postupujeme až k množinám s hornı́m indexem rovným počtu stavů (tj. v poslednı́m kroku vytvořı́me množiny pro cesty mezi stavy, které vedou přes jakékoliv stavy bez omezenı́). V poslednı́m kroku nenı́ třeba vypracovat množiny pro všechny kombinace stavů (dolnı́ index), ale pouze pro cesty z počátečnı́ho stavu do koncových stavů. 5. Výsledný regulárnı́ výraz je součet (tedy sjednocenı́) regulárnı́ch výrazů odpovı́dajı́cı́ch cestám z počátečnı́ho stavu do jednotlivých koncových stavů. Přı́klad 2.19 Zjistı́me regulárnı́ výraz odpovı́dajı́cı́ jazyku rozpoznávanému následujı́cı́m automatem: a b 1 a b 2 b 3 →1 ←2 ←3 a b 1 1 2 3 3 0 – regulárnı́ výrazy pro nejkratšı́ cesty bez mezistavů. Nejdřı́v vytvořı́me množiny Rij 2.4 VYTVOŘENÍ REGULÁRNÍHO VÝRAZU PODLE KONEČNÉHO AUTOMATU 0 =a+ε R11 0 =b R12 0 =∅ R13 0 =a R21 0 =ε R22 0 =b R23 38 0 =∅ R31 0 =∅ R32 0 =b+ε R33 Hornı́ index znamená maximálnı́ čı́slo stavu, přes který může vést cesta mezi stavy uvedenými v dolnı́m indexu (omezenı́ z hornı́ho indexu se netýká okrajových stavů cesty). 2 je regulárnı́ výraz odpovı́dajı́cı́ cestě ze stavu 1 do stavu 3 takové, že vede Napřı́klad R13 přes stavy s čı́slem maximálně 2, tedy přes stavy 1 a 2. Hornı́ index budeme postupně zvyšovat. V prvnı́m kroku se setkáme se vztahem (a + ε)∗ = a∗ a dále (a + ε) · a∗ = a∗ . 1 = (a + ε) + (a + ε) · (a + ε)∗ · (a + ε) = R11 = a + ε + a∗ = a∗ 1 R12 = b + (a + ε) · (a + ε)∗ · b = b + a∗ b = a∗ b 1 = ∅ + (a + ε) · (a + ε)∗ · ∅ = ∅ R13 1 = a + a · (a + ε)∗ · (a + ε) = a + aa∗ = aa∗ R21 1 R22 1 R23 1 R31 1 R32 1 R33 = ε + a · (a + ε)∗ · b = ε + aa∗ b = b + a · (a + ε)∗ · ∅ = b = ∅ + ∅ · (a + ε)∗ · (a + ε) = ∅ = ∅ + ∅ · (a + ε)∗ · b = ∅ = (b + ε) + ∅ · (a + ε)∗ · ∅ = b + ε V druhém kroku budeme hodně použı́vat vztah (ε + aa∗ b)∗ = (aa∗ b)∗ . 2 R11 2 R12 2 R13 2 R21 2 R22 2 R23 2 R31 2 R32 2 R33 = a∗ + a∗ b · (ε + aa∗ b)∗ · aa∗ = a∗ + a∗ b(aa∗ b)∗ aa∗ = a∗ b + a∗ b · (ε + aa∗ b)∗ · (ε + aa∗ b) = a∗ b + a∗ b(aa∗ b)∗ = a∗ b(aa∗ b)∗ = ∅ + a∗ b · (ε + aa∗ b)∗ · b = a∗ b(aa∗ b)∗ b = aa∗ + (ε + aa∗ b) · (ε + aa∗ b)∗ · aa∗ = (aa∗ b)∗ aa∗ = ε + aa∗ b + (ε + aa∗ b) · (ε + aa∗ b)∗ · (ε + aa∗ b) = (aa∗ b)∗ = b + (ε + aa∗ b) · (ε + aa∗ b)∗ · b = (aa∗ b)∗ b = ∅ + ∅ · (ε + aa∗ b)∗ · aa∗ = ∅ = ∅ + ∅ · (ε + aa∗ b)∗ · (ε + aa∗ b) = ∅ = b + ε + ∅ · (ε + aa∗ b)∗ · b = b + ε Protože máme pouze tři stavy, třetı́ krok je poslednı́. Zde nenı́ třeba vypočı́távat výrazy přes všechny dvojice stavů. Potřebujeme pouze ty cesty, které vedou z počátečnı́ho stavu do některého koncového. V automatu jsou dva koncové stavy, dostaneme tedy dva regulárnı́ výrazy. 3 = a∗ b(aa∗ b)∗ + a∗ b(aa∗ b)∗ b · (b + ε)∗ · ∅ = a∗ b(aa∗ b)∗ R12 3 = a∗ b(aa∗ b)∗ b + a∗ b(aa∗ b)∗ b · (b + ε)∗ · (b + ε) = a∗ b(aa∗ b)∗ b + a∗ b(aa∗ b)∗ b · b∗ = R13 = a∗ b(aa∗ b)∗ b · (ε + b∗ ) = a∗ b(aa∗ b)∗ bb∗ Výsledkem je regulárnı́ výraz odpovı́dajı́cı́ jazyku rozpoznávanému daným automatem: 3 3 R = R12 + R13 = = a∗ b(aa∗ b)∗ + a∗ b(aa∗ b)∗ bb∗ = a∗ b(aa∗ b)∗ · (ε + bb∗ ) = a∗ b(aa∗ b)∗ b∗ 2.4 VYTVOŘENÍ REGULÁRNÍHO VÝRAZU PODLE KONEČNÉHO AUTOMATU 39 Přı́klad 2.20 Zjistı́me regulárnı́ výraz odpovı́dajı́cı́ jazyku rozpoznávanému následujı́cı́m automatem: b 1 a a a b 3 2 →1 2 ←3 ←4 a 4 a b 4 2 3 3 3 0: Nejdřı́v vytvořı́me množiny Rij 0 R11 0 R12 0 R13 0 R14 =ε =b =∅ =a 0 R21 0 R22 0 R23 0 R24 =a =ε =b =∅ 0 R31 0 R32 0 R33 0 R34 =∅ =∅ =a+ε =∅ 0 R41 0 R42 0 R43 0 R44 =∅ =∅ =a =ε Ve čtyřech krocı́ch (čı́slo kroku je v hornı́m indexu) zjistı́me dalšı́ množiny: 1 R11 1 R12 1 R13 1 R14 = ε + ε · ε∗ · ε = ε = b + ε · ε∗ · b = b = ∅ + ε · ε∗ · ∅ = ∅ =a+ε·a=a 2 R11 2 R12 2 R13 2 R14 1 R31 1 R32 1 R33 1 R34 =∅ =∅ = a+ε+∅ = a+ε =∅ = ε + b(ab)∗ a = (ba)∗ = b + b(ab)∗ (ε + ab) = b(ab)∗ = ∅ + b(ab)∗ b = b(ab)∗ b = a + b(ab)∗ aa = a + ba(ba)∗ a = (ba)∗ a 2 R31 2 R32 2 R33 2 R34 2 R21 2 R22 2 R23 2 R24 = a + (ε + ab)(ab)∗ a = (ab)∗ a = (ab)∗ = b + (ε + ab)(ab)∗ b = (ab)∗ b = aa + (ε + ab)(ab)∗ aa = (ab)∗ aa 2 R41 2 R42 2 R43 2 R44 =∅+∅=∅ =∅ =a+ε+∅=a+ε =∅ 3 R11 3 R12 3 R13 3 R14 = (ba)∗ + ∅ = (ba)∗ = b(ab)∗ + ∅ = b(ab)∗ = b(ab)∗ b + b(ab)∗ ba∗ (a + ε) = b(ab)∗ ba∗ = (ba)∗ a + ∅ = (ba)∗ a 3 R31 3 R32 3 R33 3 R34 3 R21 3 R22 3 R23 3 R24 1 R21 1 R22 1 R23 1 R24 =a+a·ε=a = ε+aεb = ε+ab =b+∅=b = ∅ + aεa = aa = (ab)∗ a + ∅ = (ab)∗ a = (ab)∗ + ∅ = (ab)∗ = (ab)∗ b + (ab)∗ ba∗ (a + ε) = (ab)∗ ba∗ = (ab)∗ aa + ∅ = (ab)∗ aa 3 R41 3 R42 3 R43 3 R44 1 R41 1 R42 1 R43 1 R44 =∅ =∅ =a+∅=a =ε+∅=ε =∅ =∅ =a+∅=a =ε+∅=ε =∅ =∅ = a + ε + (a + ε)a∗ (a + ε) = a∗ =∅ =∅ =∅ = a + aa∗ (a + ε) = aa∗ =ε 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU 40 Ve čtvrtém kroku nás zajı́majı́ pouze cesty vedoucı́ z počátečnı́ho stavu do koncových stavů: 4 = b(ab)∗ ba∗ + (ba)∗ a · ε∗ · aa∗ = b(ab)∗ ba∗ + (ba)∗ aaa∗ = (ba)∗ bba∗ + (ba)∗ aaa∗ R13 4 = (ba)∗ a + (ba)∗ a · ε∗ · ε = (ba)∗ a R14 Regulárnı́ výraz odpovı́dajı́cı́ jazyku zadaného automatu je 4 4 R = R13 + R14 = = (ba)∗ bba∗ + (ba)∗ aaa∗ + (ba)∗ a = (ba)∗ (bba∗ + aaa∗ + a) Úkol 2.11 Zjistěte regulárnı́ výraz odpovı́dajı́cı́ jazyku následujı́cı́ch konečných automatů: a b 1 2 a a a 3 2.5 2.5.1 b a 2 1 b a a C 3 Minimalizace a normovánı́ konečného automatu Minimálnı́ konečný automat Minimalizace konečného automatu je snı́ženı́ počtu stavů při rozpoznávánı́ téhož jazyka, a to na úroveň absolutně nezbytnou vzhledem k rozpoznávanému jazyku. Účelem může být samotné snı́ženı́ počtu stavů (napřı́klad z důvodu snadnějšı́ho naprogramovánı́ či snı́ženı́ složitosti automatu) a nebo zajištěnı́ porovnatelnosti dvou automatů. Při minimalizaci automatu A s množinou stavů 1, 2, . . . , n postupujeme takto: • automat A by měl být deterministický; pokud nenı́, převedeme ho do deterministického tvaru, • podle automatu A s n stavy vytvořı́me n konečných automatů A1 , A2 , . . . , An , které jsou určeny téměř stejně (včetně přechodové funkce a koncových stavů) jako automat A, lišı́ se pouze koncovým stavem – automat Ai má počátečnı́ stav i (1 ≤ i ≤ n), • postupem popsaným v předchozı́ kapitole zjistı́me regulárnı́ výrazy odpovı́dajı́cı́ všem automatům Ai vytvořeným v předchozı́m bodu, zde vlastně zjišt’ujeme nejdůležitějšı́ charakteristiku cesty v grafu automatu z uzlu (stavu) i do koncových stavů, • regulárnı́ výrazy vhodně upravı́me, aby byly snadno porovnatelné, 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU 41 • pokud pro některé dva automaty Ai a Aj (i 6= j) vycházı́ stejný regulárnı́ výraz, znamená to, že stavy i a j jsou rovnocenné (ekvivalentnı́) v základnı́ charakteristice – na cestě z těchto dvou stavů jsou rozpoznávána tatáž slova (a žádná jiná, jde o ekvivalenci), proto jeden z nich vlastně můžeme vyřadit, a to následovně (chceme odstranit stav j a jeho funkci nahradit stavem i): – nejdřı́v všechny přechody směřujı́cı́ do stavu j přesměrujeme do stavu i, – pak můžeme stav j odstranit včetně všech přechodů, které z něho vycházejı́. Přı́klad 2.21 Minimalizujeme následujı́cı́ konečný automat: a 1 b a 2 a b 4 3 →1 2 ←3 4 a b 2 3 4 3 4 Automat je deterministický, proto můžeme začı́t se zjišt’ovánı́m regulárnı́ch výrazů. 0 R11 0 R12 0 R13 0 R14 0 R21 0 R22 0 R23 0 R24 0 R31 0 R32 0 R33 0 R34 0 R41 0 R42 0 R43 0 R44 =ε =a =∅ =∅ =∅ =ε =a =b =∅ =∅ =ε =∅ =∅ =∅ =a =b+ε 1 R11 1 R12 1 R13 1 R14 1 R21 1 R22 1 R23 1 R24 1 R31 1 R32 1 R33 1 R34 1 R41 1 R42 1 R43 1 R44 =ε =a =∅ =∅ =∅ =ε =a =b =∅ =∅ =ε =∅ =∅ =∅ =a =b+ε 2 R11 2 R12 2 R13 2 R14 2 R21 2 R22 2 R23 2 R24 2 R31 2 R32 2 R33 2 R34 2 R41 2 R42 2 R43 2 R44 =ε =a = aa = ab =∅ =ε =a =b =∅ =∅ =ε =∅ =∅ =∅ =a =b+ε 3 R11 3 R12 3 R13 3 R14 3 R21 3 R22 3 R23 3 R24 3 R31 3 R32 3 R33 3 R34 3 R41 3 R42 3 R43 3 R44 =ε =a = aa = ab =∅ =ε =a =b =∅ =∅ =ε =∅ =∅ =∅ =a =b+ε Zajı́majı́ nás pouze cesty vedoucı́ z jednotlivých stavů do koncových stavů (což je jen stav 3), proto zjistı́me pouze tyto regulárnı́ výrazy: 4 R13 4 R23 4 R33 4 R43 = aa + abb∗ a = ab∗ a = a + bb∗ a = b∗ a =ε = a + (b + ε)b∗ a = a + b∗ a = b∗ a 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU 42 4 a R4 se rovnajı́, proto jeden z nich můžeme Při porovnánı́ zjistı́me, že výrazy R23 43 odstranit. Je jedno, který zvolı́me, jen nesmı́me zapomenout všechny přechody vedoucı́ do rušeného stavu přesměrovat do ekvivalentnı́ho stavu. a 1 b a 2 a b 4 3 ⇒ a 1 a 2 b →1 2 3 a b 2 3 2 ←3 Přı́klad 2.22 Minimalizujeme následujı́cı́ konečný automat (počátečnı́ stav je prvkem množiny koncových stavů): a b a 1 2 b b a b 4 b 5 a a ↔1 2 ←3 4 ←5 3 a b 2 2 3 1 3 4 3 2 5 2 Protože v automatu je vı́ce stavů než u předchozı́ho a výpis všech pomocných množin by byl nepřehledný, vypı́šeme pouze ty množiny, které nejsou prázdné (nemajı́ hodnotu ∅). 0 =ε R11 0 =a R12 0 =b R14 0 =a+ε R22 0 =b R23 0 =b R32 0 =a+ε R33 0 =a R41 0 =ε R44 0 =b R45 0 =b R52 0 =a R53 0 =ε R55 1 =ε R11 1 =a R12 1 =b R14 1 =a+ε R22 1 =b R23 1 =b R32 1 =a+ε R33 1 =a R41 1 = aa R42 1 = ε + ab R44 1 =b R45 1 =b R52 1 =a R53 1 =ε R55 2 R11 2 R12 2 R13 2 R14 =ε = aa∗ = aa∗ b =b 2 R22 2 R23 2 R32 2 R33 = a∗ = a∗ b = ba∗ = a + ε + ba∗ b 3 =ε R11 3 = aa∗ + aa∗ (a + ba∗ b)∗ ba∗ R12 3 = aa∗ b(a + ba∗ b)∗ R13 2 R41 2 R42 2 R43 2 R44 =a = aaa∗ = aaa∗ b = ε + ab 2 R45 2 R52 2 R53 2 R55 =b = ba∗ = a + ba∗ b =ε 3 =b R14 3 = a∗ + a∗ b(a + ba∗ b)∗ ba∗ R22 3 = a∗ b(a + ba∗ b)∗ R23 2.5 3 R32 3 R33 3 R41 3 R42 3 R43 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU = (a + ba∗ b)∗ ba∗ = (a + ba∗ b)∗ =a = aaa∗ + aaa∗ b(a + ba∗ b)∗ ba∗ = aaa∗ b(a + ba∗ b)∗ 3 R44 3 R45 3 R52 3 R53 3 R55 43 = ε + ab =b = ba∗ = (a + ba∗ b)(a + ba∗ b)∗ =ε 4 = ε + b(ab)∗ a = (ba)∗ R11 4 = aa∗ + aa∗ (a + ba∗ b)∗ ba∗ + b(ab)∗ (aaa∗ + aaa∗ b(a + ba∗ b)∗ ba∗ ) R12 4 = aa∗ b(a + ba∗ b)∗ + b(ab)∗ aaa∗ b(a + ba∗ b)∗ = (ε + b(ab)∗ a)(aa∗ b(a + ba∗ b)∗ ) = R13 = (ba)∗ aa∗ b(a + ba∗ b)∗ 4 R14 = b 4 = b(ab)∗ b = (ba)∗ bb R15 4 = a∗ + a∗ b(a + ba∗ b)∗ ba∗ R22 4 = a∗ b(a + ba∗ b)∗ R23 4 R32 = (a + ba∗ b)∗ ba∗ 4 = (a + ba∗ b)∗ R33 4 = (ab)∗ a R41 4 = (ab)∗ (aaa∗ + aaa∗ b(a + ba∗ b)∗ ba∗ ) R42 4 = (ab)∗ aaa∗ b(a + ba∗ b)∗ R43 4 = (ab)∗ R44 4 = (ab)∗ b R45 4 = ba∗ R52 4 = (a + ba∗ b)(a + ba∗ b)∗ R53 4 =ε R55 V poslednı́m kroku se zaměřı́me pouze na cesty končı́cı́ v některém koncovém stavu (to jsou stavy 1, 3 a 5), vypı́šeme i prázdné množiny. 5 = (ba)∗ R11 5 = (ba)∗ aa∗ b(a + ba∗ b)∗ + R13 +(ba)∗ bb(a + ba∗ b)(a + ba∗ b)∗ 5 R15 = (ba)∗ bb 5 =∅ R21 5 R23 = a∗ b(a + ba∗ b)∗ 5 =∅ R25 5 =∅ R31 5 = (a + ba∗ b)∗ R33 5 =∅ R35 5 = (ab)∗ a R41 5 = (ab)∗ aaa∗ b(a + ba∗ b)∗ + R43 +(ab)∗ b(a + ba∗ b)(a + ba∗ b)∗ 5 R45 = (ab)∗ b 5 =∅ R51 5 = (a + ba∗ b)(a + ba∗ b)∗ R53 5 =ε R55 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU 44 Zbývá zjistit jazyky jednotlivých automatů A1 , A2 , . . . , A5 (odlišujı́cı́ch se pouze svým počátečnı́m stavem – 1, 2, . . . , 5): 5 + R5 + R5 = L(A1 ) = L(A) = R11 15 13 = (ba)∗ + (ba)∗ aa∗ b(a + ba∗ b)∗ + bb(a + ba∗ b)(a + ba∗ b)∗ + bb = = (ba)∗ + (ba)∗ aa∗ b(a + ba∗ b)∗ + bb(a + ba∗ b)∗ = = (ba)∗ + (ba)∗ (aa∗ b + bb)(a + ba∗ b)∗ 5 + R5 + R5 = a∗ b(a + ba∗ b)∗ L(A2 ) = R21 23 25 5 + R5 + R5 = (a + ba∗ b)∗ L(A3 ) = R31 33 35 5 + R5 + R5 = (ab)∗ a + (ab)∗ aaa∗ b(a + ba∗ b)∗ + (ab)∗ b(a + ba∗ b)∗ = L(A4 ) = R41 43 45 = (ab)∗ a + (ab)∗ (aaa∗ b + b)(a + ba∗ b)∗ 5 + R5 + R5 = (a + ba∗ b)∗ L(A5 ) = R51 53 55 Jazyky L(A3 ) a L(A5 ) jsou stejné, proto jeden z těchto stavů můžeme odstranit. a b a 1 2 b b a b 4 Úkol a 3 b 5 a ⇒ a b a 1 2 b b a b 4 a 3 ↔1 2 ←3 4 a b 2 2 3 1 4 3 2 3 2.12 C 1. Minimalizujte následujı́cı́ konečný automat: a 1 b b a 2 a b 4 3 a 5 b →1 2 ←3 4 5 a b 2 3 4 2 2 3 2 5 2. Následujı́cı́ (nedeterministický) konečný automat • převed’te na deterministický (po převodu a odstraněnı́ nepotřebných stavů by měl mı́t čtyři stavy), • vhodně přeznačte stavy (čı́sla od 1) a určete jazyk tohoto konečného automatu, • minimalizujte ho. 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU b a a b 1 a 3 →1 ←2 3 2 45 a b 1, 2 3 1 1 Při úpravách regulárnı́ch výrazů lze využı́t napřı́klad těchto vztahů: • b(ab)∗ = (ba)∗ b • (ab)∗ a = a(ba)∗ • b(ab)∗ a = (ba)∗ ba • ε + b(ab)∗ a = ε + (ba)∗ ba = (ba)∗ • ε + (ba)∗ aa∗ b ((ba)∗ aa∗ b)∗ = = ((ba)∗ aa∗ b)∗ Pozor, sice platı́ rovnost ε + b∗ a = b∗ , ale napřı́klad regulárnı́ výrazy ε + b∗ ac∗ a b∗ c∗ nejsou ekvivalentnı́! 2.5.2 Normovaný konečný automat Účelem normovánı́ (čehokoliv) je uvést daný objekt (prvek, automat, atd.) do stavu s určitými žádanými vlastnostmi, a to za účelem snadnějšı́ho prováděnı́ dalšı́ch operacı́ s tı́mto objektem (napřı́klad porovnávánı́ nebo programovánı́). Když normujeme konečný automat, chceme, aby měl vhodně označené stavy (tedy normujeme označenı́ stavů) – stavy by měly být pojmenovány čı́sly od 1 a navı́c by jejich označenı́ mělo být seřazeno. Kromě vlastnı́ho označenı́ majı́ stavy jen jedinou dalšı́ charakteristiku – regulárnı́ výraz odpovı́dajı́cı́ cestě z daného stavu do koncových stavů. Tuto charakteristiku použijeme pro vytvořenı́ metriky při řazenı́ označenı́ stavů. Postupujeme takto: • minimalizujeme konečný automat, • pro každý stav určı́me regulárnı́ výraz cesty z tohoto stavu do koncových stavů, • v regulárnı́ch výrazech najdeme nejmenšı́ slova, • stavy porovnáváme takto: 1. stav, jehož regulárnı́ výraz má kratšı́ nejmenšı́ slovo, dostane vyššı́ index než stav s delšı́m nejmenšı́m slovem, napřı́klad stav s nejmenšı́m slovem ab bude mı́t vyššı́ čı́slo v označenı́ než stav s nejmenšı́m slovem bba, 2. pokud dva stavy majı́ nejmenšı́ slova stejně dlouhá, pak zvolı́me navı́c dalšı́ metriku – abecedu, napřı́klad stav s nejmenšı́m slovem ab bude mı́t vyššı́ čı́slo v označenı́ než stav s nejmenšı́m slovem bb, 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU 46 3. pokud se nejmenšı́ slova dvou stavů shodujı́ (tj. předchozı́ metriky selhávajı́), pak v regulárnı́ch výrazech těchto stavů hledáme druhé nejkratšı́ slovo a to porovnáváme, pokud se opět shodujı́, hledáme třetı́, atd., dokud nenajdeme rozdı́l. Přestože poslednı́ uvedený způsob porovnánı́ může trvat hodně dlouho, u minimalizovaného automat je vždy konečný, protože regulárnı́ výrazy pro žádné dva stavy nejsou ekvivalentnı́ (kdyby byly ekvivalentnı́, jeden z nich bychom vyřadili při minimalizaci). Přı́klad 2.23 Seřadı́me následujı́cı́ regulárnı́ výrazy podle výše uvedených kritériı́. Reg. výraz Nejkratšı́ slova ba(ba)∗ ba, baba, bababa, . . . aab, aabb, aabbb, . . . ab, abab, ababab, . . . aab, aaba, aabaa, . . . ab, abba, abbaba, . . . ab, aab, bab, aaab, baab, bbab, . . . b, bb, aab, bbb, baab, aaaab, . . . b, a, ab, ba, aab, aab, bba, . . . ab, ba ε, ab, ba, abab, abba, baab, baba, . . . aabb∗ ab(ab)∗ aaba∗ ab(ba)∗ b∗ aa∗ b b∗ (aa)∗ b a∗ b + b∗ a ab + ba (ab + ba)∗ Zjištěné pořadı́ Přidělený index 8 10 6 9 7 5 3 2 4 1 3 1 5 2 4 6 8 9 7 10 Přı́klad 2.24 Znormujeme automat, který jsme zı́skali minimalizacı́ v přı́kladu 2.22 na straně 42. a b a 1 2 b b a b 4 a 3 ↔1 2 ←3 4 a b 2 2 3 1 4 3 2 3 Využijeme také regulárnı́ výrazy pro jednotlivé stavy: 5 + R5 + R5 = (ba)∗ + (ba)∗ (aa∗ b + bb)(a + ba∗ b)∗ L(A1 ) = L(A) = R11 13 15 5 + R5 + R5 = a∗ b(a + ba∗ b)∗ L(A2 ) = R21 23 25 5 + R5 + R5 = (a + ba∗ b)∗ L(A3 ) = R31 33 35 5 + R5 + R5 = (ab)∗ a + (ab)∗ (aaa∗ b + b)(a + ba∗ b)∗ L(A4 ) = R41 43 45 2.5 MINIMALIZACE A NORMOVÁNÍ KONEČNÉHO AUTOMATU 47 Z těchto regulárnı́ch výrazů vybereme nejkratšı́ slova: Stav 1 2 3 4 Nejkratšı́ slova Nové označenı́ stavu ab, bb, ba, aba, . . . b, ba, ab, . . . ε, a, bb, . . . a, b, . . . 1 2 4 3 Stavy přeznačı́me a vše ostatnı́ zachováme. Po přeznačenı́ dostaneme tento konečný automat: a b a 1 2 b b Úkol a b 3 a 4 ↔1 2 ←4 3 a b 2 2 4 1 3 4 2 4 2.13 1. Setřid’te podle výše zadaných kritériı́ následujı́cı́ch 16 regulárnı́ch výrazů: • a(a + b)a • a∗ (a + b)a∗ • (a∗ b + a)∗ • a(a + b)∗ a • a∗ b + a • (a∗ b + b)∗ • a(a + b)∗ b • a∗ b • (b∗ a + b)∗ • a∗ (a + b)a • b∗ a • (b∗ a + b)∗ Pozor, může se stát, že některé dva regulárnı́ výrazy jsou ekvivalentnı́. 2. Podle konečného automatu v zadánı́ přı́kladu 2.20 na straně 39 dopočtěte zbývajı́cı́ 4 , minimalizujte tento automat a normujte ho. množiny Rij 3. Podle konečného automatu v zadánı́ přı́kladu 2.14 na straně 28 vytvořte všechny k , minimalizujte (pokud nenı́ minimálnı́) a normujte ho. potřebné množiny Rij C Kapitola 3 K zásobnı́kovým automatům Zde se krátce vrátı́me k zásobnı́kovým automatům, které bychom již měli znát. Soustředı́me se pouze na jejich vlastnosti souvisejı́cı́ s množinovými operacemi. 3.1 Využitı́ uzávěrových vlastnostı́ při konstrukci automatu Synchronizace dvou částı́ slova (tj. u automatu zjištěnı́, zda mezi jejich délkami je přı́má úměrnost) se provádı́ pomocı́ zásobnı́ku. V prvnı́ synchronizované části do zásobnı́ku ukládáme symboly, při čtenı́ druhé části naopak symboly ze zásobnı́ku vyjı́máme. Postup při navrhovánı́ automatu: 1. Promyslı́me si, co by automat měl provádět v různých částech rozpoznávaného slova. 2. Určı́me stavy, které budou pro jednotlivé části slova použı́vány, platı́, že odlišné chovánı́ pro různé části slova znamená odlišné stavy. 3. Určı́me, jak se bude v různých stavech při konkrétnı́m symbolu na vrcholu zásobnı́ku chovat automat. 4. Zajistı́me správné ukončenı́ rozpoznánı́ slova, dále měli bychom otestovat, zda automat správně přijı́má nebo naopak nepřijı́má prázdné slovo, také provedeme otestovánı́ několika typických slov patřı́cı́ch do jazyka a naopak do jazyka nepatřı́cı́ch (pro kontrolu). Přı́klad 3.1 Sestrojı́me zásobnı́kový automat pro následujı́cı́ jazyk, využijeme uzávěrové vlastnosti (konkrétně operaci sjednocenı́ jazyků). L = {ai bj ck : i, j, k ≥ 1, i = j nebo j = k} Nejdřı́v sestrojı́me zásobnı́kové automaty pro jazyky L1 = {ai bi ck : i, k ≥ 1} L2 = {ai bk ck : i, k ≥ 1} 48 P 3.1 VYUŽITÍ UZÁVĚROVÝCH VLASTNOSTÍ PŘI KONSTRUKCI AUTOMATU 49 Vidı́me, že prvnı́ jazyk splňuje podmı́nku i = j kladenou v původnı́m jazyce, druhý jazyk zase podmı́nku j = k. Pro oba jazyky lze velmi jednoduše sestrojit zásobnı́kový automat, to již umı́me. Když pak sestrojı́me sjednocenı́, zı́skáme automat rozpoznávajı́cı́ jazyk L1 ∪ L2 = L. Nejdřı́v sestrojı́me automat A1 rozpoznávajı́cı́ jazyk L1 . Použijeme tyto stavy: • q0 . . . začátek výpočtu, načı́táme symboly a ze vstupu, ukládáme je do zásobnı́ku (zásobnı́k se v každém kroku prodlužuje o 1) • q1 . . . načı́táme symboly b ze vstupu, pro každý symbol b vyndáme ze zásobnı́ku jeden symbol a, v každém kroku se zásobnı́k krátı́ o 1 • q2 . . . načı́táme symboly c ze vstupu, v zásobnı́ku by měl být pouze symbol Z0 (v každém kroku je ze zásobnı́ku vyndán a hned vrácen) • qf . . . koncový stav, v tomto stavu končı́ výpočet (pokud je vstup přečtený a přı́padně zásobnı́k prázdný, pak výpočet končı́ přijetı́m slova, jinak jeho odmı́tnutı́m). A1 = (Q1 , Σ, Γ1 , δ1 , q0 , Z1 , F1 ) = ({q0 , q1 , q2 , qf }, {a, b, c}, {Z1 , a}, δ1 , q0 , Z1 , {qf }) δ1 (q0 , a, Z1 ) = (q0 , aZ1 ) . . . δ1 (q0 , a, a) = (q0 , aa) . . . . . δ1 (q0 , b, a) = (q1 , ε) . . . . . . . δ1 (q1 , b, a) = (q1 , ε) . . . . . . . δ1 (q1 , c, Z1 ) = (q2 , Z1 ) . . . . δ1 (q2 , c, Z1 ) = (q2 , Z1 ) . . . . δ1 (q2 , ε, Z1 ) = (qf , ε) . . . . . prvnı́ rozpoznané pı́smeno musı́ být a, vložı́me je do zásobnı́ku načı́táme symboly a, ukládáme do zásobnı́ku prvnı́ b, smažeme jedno a ze zásobnı́ku, přesun do stavu q1 pro každé b ze vstupu odstranı́me jedno a ze zásobnı́ku třetı́ část slova, v zásobnı́ku už musı́ být jen Z1 načı́táme symboly c, zásobnı́k se neměnı́ (co vyndáme, vrátı́me) zakončenı́, přechod do koncového stavu Všimněte si poslednı́ho řádku předpisu – ε neznamená, že vstup byl přečten, znamená pouze to, že si vstupu „nevšı́máme“; tento řádek může být použit i v přı́padě, že na vstupu zůstaly nepřečtené symboly, ale v tom přı́padě přecházı́me do koncového stavu, pro který nemáme žádný předpis, tudı́ž by výpočet nemohl skončit úspěchem. Z toho vyplývá, že tento řádek lze úspěšně (v posloupnosti konfiguracı́ vedoucı́ ke koncové konfiguraci) použı́t pouze tehdy, když je celý vstup přečtený. Poslednı́ řádek by také mohl vypadat takto: δ1 (q2 , ε, Z1 ) = (qf , Z1 ) a to v přı́padě, že bychom sestrojovali automat, který neodstraňuje symbol konce zásobnı́ku. Dále sestrojı́me automat A2 rozpoznávajı́cı́ jazyk L2 . Význam stavů bude podobný. A2 = (Q2 , Σ, Γ2 , δ2 , r0 , Z2 , F2 ) = ({r0 , r1 , r2 , rf }, {a, b, c}, {Z2 , b}, δ2 , r0 , Z2 , {rf }) δ1 (r0 , a, Z2 ) = (r0 , Z2 ) . . . . δ1 (r0 , b, Z2 ) = (r1 , bZ2 ) . . . δ1 (r1 , b, b) = (r1 , bb) . . . . . . δ1 (r1 , c, b) = (r2 , ε) . . . . . . . δ1 (r2 , c, b) = (r2 , ε) . . . . . . . δ1 (r2 , ε, Z2 ) = (rf , ε) . . . . . prvnı́ rozpoznané pı́smeno musı́ být a, zásobnı́k neměnı́me prvnı́ symbol b, uložı́me do zásobnı́ku načı́táme symboly b, ukládáme do zásobnı́ku pro c ze vstupu odstranı́me jedno b ze zásobnı́ku dto., opakovaně zakončenı́, přechod do koncového stavu L 3.1 VYUŽITÍ UZÁVĚROVÝCH VLASTNOSTÍ PŘI KONSTRUKCI AUTOMATU 50 Zbývá sestrojit výsledný automat, který bude rozpoznávat jazyk, jež je sjednocenı́m předchozı́ch dvou (tj. slova patřı́cı́ bud’ do L1 nebo do L2 ). Postup je principiálně podobný tomu, co jsme prováděli pro konečné automaty (včetně přidánı́ nového stavu s0 ), ale máme to jednoduššı́ v tom směru, že lze provádět „prázdné“ přechody – bez čtenı́ ze vstupu, a naopak postup mı́rně komplikuje zásobnı́k. Prvnı́m krokem v novém automatu bude nasměrovánı́ výpočtu bud’ do prvnı́ho, a nebo do druhého původnı́ho automatu (správně bychom měli hovořit o převzetı́ přechodové funkce). Tedy z počátečnı́ho stavu s0 přecházı́me bud’ do stavu q0 a nebo do stavu r0 , podle toho, zda je na vstupu slovo z jazyka L1 nebo L2 . Protože v tomto kroku nebude čten vstup (a ostatně podle prvnı́ho symbolu stejně nelze rozlišit, do kterého z těchto jazyků slovo patřı́), bude se jednat o nedeterministický krok. Zároveň s přechodem do jednoho z původnı́ch počátečnı́ch stavů se změnı́ i symbol na zásobnı́ku. Zásobnı́kový symbol výsledného automatu bude nahrazen zásobnı́kovým symbolem Z1 nebo Z2 . A = ({s0 } ∪ Q1 ∪ Q2 , Σ, {Z} ∪ Γ1 ∪ Γ2 , δ, s0 , Z, F1 ∪ F2 ) = = ({s0 , q0 , q1 , q2 , qf , r0 , r1 , r2 , rf }, {a, b, c}, {Z, Z1 , Z2 , a, b}, δ, s0 , Z, {qf , rf }) Přechodová funkce – nejdřı́v pro nový stav: δ(s0 , ε, Z) = {(q0 , Z1 ), (r0 , Z2 )} Zbylou část předpisu δ-funkce přejmeme z automatů A1 a A2 : ( δ(p, x, Y ) = δ1 (p, x, Y ) : p ∈ Q1 , x ∈ Σ, Y ∈ Γ1 δ2 (p, x, Y ) : p ∈ Q2 , x ∈ Σ, Y ∈ Γ2 Ukážeme si rozpoznánı́ jednoho ze slov jazyka L(A), slova a3 b3 c2 : (s0 , aaabbbcc, Z) ` (q0 , aaabbbcc, Z1 ) ` (q0 , aabbbcc, aZ1 ) ` (q0 , abbbcc, aaZ1 ) ` ` (q0 , bbbcc, aaaZ1 ) ` (q1 , bbcc, aaZ1 ) ` (q1 , bcc, aZ1 ) ` (q1 , cc, Z1 ) ` (q2 , c, Z1 ) ` ` (q2 , ε, Z1 ) ` (qf , ε, ε) Slovo ε nepatřı́ do jazyka rozpoznávaného automatem, tedy bychom se při výpočtu neměli dostat do koncové konfigurace, je lepšı́ to ověřit: (s0 , ε, Z) ` (q0 , ε, Z1 ) ` nelze pokračovat, slovo nepatřı́ do jazyka Nejkratšı́ slovo jazyka je abc: (s0 , abc, Z) ` (r0 , abc, Z2 ) ` (r0 , bc, Z2 ) ` (r1 , c, bZ2 ) ` (r2 , ε, Z2 ) ` (rf , ε, ε) Úkol 3.1 1. Sestrojte zásobnı́kové automaty rozpoznávajı́cı́ tyto jazyky: (a) L1 = nai bj aj bi : i, j ≥ 1 o (b) L2 = wwR : w ∈ {a, b}∗ (c) L3 = ai bj : 0 < i < j C 3.2 UZÁVĚROVÉ VLASTNOSTI JAKO KRITÉRIUM BEZKONTEXTOVOSTI n (d) L4 = ai cbj cak : i, j, k ≥ 0, i + j = k 51 o 2. Využijte uzávěrové vlastnosti (konkrétně sjednocenı́) při konstrukci zásobnı́kových automatů rozpoznávajı́cı́ch tyto jazyky: n n n an : n ≥ 1} (a) L1 = {a n b : n ≥ 1} ∪ {b o (b) L2 = wcwR : w ∈ {a, b}∗ ∪ {cn bn : n ≥ 0} n (c) L3 = ai cbj cak : i, j, j ≥ 0, i = j + k nebo i + j = k 3.2 o Uzávěrové vlastnosti jako kritérium bezkontextovosti Při důkazu, že daný jazyk nenı́ bezkontextový, se ve většině přı́padů využı́vajı́ spı́še gramatiky. Výjimkou je napřı́klad důkaz s využitı́m operace průniku s regulárnı́m jazykem. Vı́me, že třı́da bezkontextových jazyků je uzavřena vzhledem k průniku s regulárnı́m jazykem, nynı́ toho využijeme. Přı́klad 3.2 Dokážeme, že následujı́cı́ jazyk nenı́ bezkontextový: L = {w ∈ {a, b, c}∗ : |w|a = |w|b = |w|c } Do tohoto jazyka patřı́ napřı́klad tato slova (v každém slově je stejný počet symbolů a, b a c, ale o jejich pořadı́ ve slově předpis nic neřı́ká): L = {ε, abc, acb, bac, bca, cab, cba, aabbcc, aaccbb, aabcbc, . . .} Provedeme důkaz sporem. Předpokládejme, že L je bezkontextový jazyk. Potom by jeho průnik s kterýmkoliv regulárnı́m jazykem byl opět bezkontextovým jazykem. Stačı́ tedy najı́t jediný regulárnı́ jazyk takový, že jeho průnik s jazykem L nebude bezkontextový jazyk. Vybereme napřı́klad R = a∗ b∗ c∗ , což je regulárnı́ výraz popisujı́cı́ regulárnı́ jazyk, kde je určeno pořadı́ symbolů ve slově (nejdřı́v symboly a, pak b, potom teprve c), ale naopak nepředepisuje vzájemný poměr počtu různých symbolů ve slově. Opět pro ilustraci vypı́šeme několik slov jazyka R: R = {ε, a, b, c, aa, ab, ac, bb, bc, cc, a3 , aab, aac, abb, abc, acc, b3 , bbc, bcc, c3 , . . .} Průnikem jazyků L a R bude jazyk, který splňuje jak vlastnost rovnosti počtu symbolů a, b a c ve slově, tak i vlastnost uspořádánı́ symbolů: n o L ∩ R = w ∈ {a, b, c}∗ : |w|a = |w|b = |w|c , w = ai bj ck , i, j, k ≥ 0 = {an bn cn : n ≥ 0} O tomto jazyce však vı́me, že nenı́ bezkontextový. Dospěli jsme ke sporu, dokázali jsme, že L nenı́ bezkontextový. P Kapitola 4 Regulárnı́ gramatiky 4.1 Opakovánı́ S regulárnı́mi gramatikami jsme se seznámili v předchozı́m semestru. Nejdřı́v si zopakujeme některé základnı́ pojmy a postupy, které by se pro nás měly stát již zcela triviálnı́mi, abychom mohli navázat v pokročilejšı́ch tématech. Připomeňme si, že regulárnı́ gramatika s předpisem G = (N, T, P, S) má pravidla v této formě: • A → aB, A, B ∈ N, a ∈ T • A → a, A ∈ N, a ∈ T • S → ε je jediné přı́pustné ε-pravidlo, pokud existuje, tak se S nesmı́ nacházet na pravé straně žádného pravidla. Pokud máme sestrojit automat, který rozpoznává jazyk generovaný regulárnı́ gramatikou G = (N, T, P, S), půjde samozřejmě o konečný automat a budeme postupovat takto: 1. Označme A = (Q, Σ, δ, q0 , F ). 2. Abeceda je zřejmá – slova generovaná gramatikou jsou nad abecedou T , proto nad touto abecedou budou také slova rozpoznávaná automatem: Σ=T 3. Jako množinu stavů použijeme množinu neterminálů, navı́c přidáme stav X, který bude sloužit jako koncový stav: Q = N ∪ {X} 4. Počátečnı́m stavem automatu bude stav vzniklý ze startovacı́ho symbolu S, tedy q0 = S 5. Množina ( koncových stavů: {X} : ε ∈ / L(G) F = {S, X} : ε ∈ L(G) 52 P 4.1 OPAKOVÁNÍ 6. Zbývá δ-funkce: δ(A, a) = B ⇔ δ(A, a) = X ⇔ 53 (A → aB) ∈ P, A, B ∈ N, a ∈ T (A → a) ∈ P, A, B ∈ N, a ∈ T Přechodovou funkci tedy tvořı́me podle pravidel. Mnemotechnická pomůcka: všimněte si, že v předpisu δ-funkce jsou jednotlivé prvky ve stejném pořadı́, ve kterém jsou i v pravidle. Přı́klad 4.1 Podle zadané regulárnı́ gramatiky sestrojı́me konečný automat. G = ({S, A, B}, {a, b}, P, S) S → aA | bB A → aA | aB | a B → bA | b Gramatika neobsahuje ε-pravidlo, proto množina koncových stavů bude obsahovat pouze nový stav X. Automat vypadá takto: A = ({S, A, B, X}, {a, b}, δ, S, {X}) δ(S, a) = {A} δ(S, b) = {B} δ(A, a) = {A, B} δ(A, b) = {X} δ(B, b) = {A, X} a S b a A b b b b B X → S A B ← X a b A A, B B X A, X Přı́klad 4.2 Podle zadané regulárnı́ gramatiky sestrojı́me konečný automat. G = ({S, A, B}, {a, b}, P, S), v množině P jsou pravidla S → bA | ε A → aB | a B → bA | b V jazyce generovaném gramatikou je také prázdné slovo ε a toto slovo musı́ přijı́mat i konečný automat, který vytvořı́me. Počátečnı́ stav automatu přidáme do množiny koncových stavů. A = ({S, A, B, X}, {a, b}, δ, S, {S, X}) δ(S, b) = {A} δ(A, a) = {B, X} δ(B, b) = {A, X} a b S A B b a b X a ↔ S A B ← X b A B, X A, X L 4.2 NORMÁLNÍ FORMA REGULÁRNÍCH GRAMATIK 54 Jazyk generovaný gramatikou a rozpoznávaný automatem je L(G) = L(A) = ε + b(ab)∗ a + b(ab)∗ ab = ε + b(ab)∗ a(ε + b) To by pro připomenutı́ stačilo. Podobné postupy pro různé základnı́ operace s regulárnı́mi gramatikami (včetně vytvořenı́ gramatiky podle konečného automatu) najdeme ve sbı́rce přı́kladů pro předmět Teorie jazyků a automatů I. 4.2 Normálnı́ forma regulárnı́ch gramatik Normálnı́ forma gramatiky obecně je předpis určujı́cı́ formu řetězce na levé a pravé straně pravidla gramatiky. V přı́padě regulárnı́ch gramatik máme pouze jednu normálnı́ formu, a to právě tu, kterou jsme dosud běžně použı́vali. Ve skutečnosti se totiž můžeme setkat s regulárnı́mi gramatikami v obecnějšı́m formátu a pak je třeba takovou gramatiku převést do normálnı́ formy. Obecná regulárnı́ gramatika G = (N, T, P, S) může vypadat takto: • A → a1 a2 . . . an B, A, B ∈ N, ai ∈ T, 1 ≤ i ≤ n • A → a1 a2 . . . an , A ∈ N, ai ∈ T, 1 ≤ i ≤ n • může existovat pravidlo S → ε pokud se symbol S nacházı́ na pravé straně žádného pravidla. Také se můžeme setkat s gramatikami, kde se vyskytujı́ ε-pravidla i u jiných neterminálů než jen u startovacı́ho symbolu, přı́padně mohou existovat tzv. jednoduchá pravidla. Jednoduché pravidlo je pravidlo ve tvaru A → B, tj. neterminál je přepisován jiným neterminálem (pozor, pravidlo přepisujı́cı́ neterminál na terminál nenı́ jednoduché!). Regulárnı́ gramatika G = (N, T, P, S) je v normálnı́ formě, pokud všechna jejı́ pravidla splňujı́ tyto podmı́nky: • A → aB, A, B ∈ N, a ∈ T • A → a, A ∈ N, a ∈ T • může existovat pravidlo S → ε pokud se symbol S nacházı́ na pravé straně žádného pravidla. Pokud máme regulárnı́ gramatiku převést do normálnı́ formy, je třeba postupovat takto: 1. Odstranı́me ε-pravidla, která neodpovı́dajı́ předpisu (pozor, nelze je pouze smazat, vytvářı́me přece ekvivalentnı́ gramatiku). 2. Odstranı́me jednoduchá pravidla, tj. pravidla typu A → B, tedy na pravé i levé straně je jeden neterminál. 4.2 NORMÁLNÍ FORMA REGULÁRNÍCH GRAMATIK 55 3. Zkrátı́me přı́liš dlouhá pravidla. Přı́klad 4.3 Následujı́cı́ gramatiku převedeme do normálnı́ formy pro regulárnı́ gramatiky. G = ({S, A, B}, {a, b}, P, S) S → abA | b A → aaB | a B → S | bba | ε Normálnı́ formě neodpovı́dá většina pravidel. Předevšı́m tu máme ε-pravidlo, které by u symbolu B nemělo být, dále jedno jednoduché pravidlo a potom je třeba několik pravidel zkrátit. Postupně provedeme tyto úpravy: 1. Odstranı́me ε-pravidlo: Pravidlo B → ε odstranı́me, ale abychom zajistili ekvivalenci nové a původnı́ gramatiky, musı́me „simulovat“ použitı́ tohoto pravidla. Symbol B se nacházı́ na pravé straně pravidla A → aaB, pokud by toto pravidlo bylo použito v derivaci a v následujı́cı́m kroku bychom použili ε-pravidlo, derivace by vypadala takto: . . . ⇒ γA ⇒ γaaB ⇒ γaa ⇒ . . . Proto přidáme nové pravidlo: A → aa Obrazem uvedené derivace z původnı́ gramatiky by pro novou gramatiku bylo: . . . ⇒ γA ⇒ γaa ⇒ . . . Kdyby se symbol B vyskytoval na pravé straně vı́ce pravidel, provedli bychom totéž pro všechna tato pravidla. Po změně jsou pravidla následujı́cı́: S → abA | b A → aaB | aa | a B → S | bba 2. Odstranı́me jednoduché pravidlo: Je zde pouze jedno jednoduché pravidlo, B → S. Opět z důvodu zajištěnı́ ekvivalence nové gramatiky s původnı́ budeme vycházet z možných derivacı́: . . . ⇒ γB ⇒ γS ⇒ γabA ⇒ . . . . . . ⇒ γB ⇒ γS ⇒ γb ⇒ . . . Pravou stranu jednoduchého pravidla proto nahradı́me pravými stranami pravidel přepisujı́cı́ch symbol S. Mı́sto pravidla B → S zı́skáme pravidla B → abA | b Všechna pravidla: S → abA | b A → aaB | aa | a 4.2 NORMÁLNÍ FORMA REGULÁRNÍCH GRAMATIK 56 B → abA | b | bba 3. Zkrátı́me dlouhá pravidla: Z důvodu zachovánı́ ekvivalence gramatik použijeme nové neterminály. U předchozı́ch úprav jsme vlastně derivaci krátili, ted’ ji budeme prodlužovat. Napřı́klad mı́sto následujı́cı́ derivace . . . ⇒ γB ⇒ γabA bude derivace . . . ⇒ γB ⇒ γaZ1 ⇒ γabA Což mı́sto B → abA předpokládá pravidla B → aZ1 Z1 → bA Ted’ už můžeme zkompletovat celou gramatiku: G0 = ({S, A, B, Z1 , Z2 , Z3 , Z4 }, {a, b}, P 0 , S) S → aZ1 | b Z1 → bA A → aZ2 | aZ3 | a Z2 → aB Z3 → a B → aZ1 | b | bZ4 Z4 → bZ3 Jak vidı́me, gramatika je v normálnı́ formě pro regulárnı́ gramatiky a navı́c L(G0 ) = L(G), tedy gramatiky jsou ekvivalentnı́. Přı́klad 4.4 Následujı́cı́ gramatiku opět převedeme do normálnı́ formy pro regulárnı́ gramatiky. G = ({S, A}, {0, 1, 2}, P, S) S → 0A | 1S | ε A → 2A | 1S | 2 Pravidlo S → ε by samo o sobě nemuselo vadit, ale vidı́me, že symbol S je na pravé straně dokonce dvou pravidel. Řešenı́m je vytvořit nový startovacı́ symbol, který se na začátku derivace bude chovat stejně jako S (tj. pro tento symbol budou stejná pravidla jako pro S), ale samozřejmě nebude na pravé straně žádného pravidla. S 0 → 0A | 1S | ε Pak ε-pravidlo vyřešı́me stejně jako v předchozı́m přı́kladu. Po úpravě: G0 = ({S 0 , S, A}, {0, 1, 2}, P, S 0 ) S 0 → 0A | 1S | 1 | ε S → 0A | 1S | 1 A → 2A | 1S | 1 | 2 (pravidlo S 0 → 1 jsme přidali při eliminaci pravidla S → ε) 4.2 NORMÁLNÍ FORMA REGULÁRNÍCH GRAMATIK 57 Mohlo by se zdát, že normálnı́ forma pro regulárnı́ gramatiky je zbytečná, vždyt’ zvyšuje množstvı́ pravidel (všimněte si, jakým způsobem se krátila pravidla). Normálnı́ forma má význam předevšı́m v úlohách, kde provádı́me srovnánı́, využı́váme gramatiku v některém algoritmu či důkazu (když se nad tı́m zamyslı́te, algoritmus a důkaz majı́ mnoho společného), a také pokud podle gramatiky vytvářı́me ekvivalentnı́ konečný automat. Úkol 4.1 Následujı́cı́ gramatiky převed’te do normálnı́ formy pro regulárnı́ gramatiky: G1 = ({S, A, B}, {a, b}, P, S) S → aB | bA A → bbB | ε B → aA | b G2 = ({S, A, B}, {a, b, c}, P, S) S → bbA | aS | ε A → abaA | bB | a B → cabS | bc L C Kapitola 5 Bezkontextové gramatiky 5.1 Derivačnı́ strom Derivačnı́ strom je vlastně graf, který je stromem (má jediný kořen, každý uzel kromě kořene má právě jednoho předka a hrany jsou orientované, i když orientaci neznačı́me), tvořený podle pravidel gramatiky použitých v derivaci, podle které je derivačnı́ strom vytvořen. Přı́klad 5.1 Sestrojı́me bezkontextovou gramatiku pro jazyk L = {0n 102n 1i | i, n ≥ 1} a vytvořı́me derivaci slova 0010000111 a derivačnı́ strom k této derivaci. G = ({S, A, B}, {0, 1}, P, S) S → AB A → 0A00 | 0100 B → 1B | 1 Derivace slova 0010000111 je následujı́cı́: S ⇒ AB ⇒ 0A00B ⇒ 0010000B ⇒ 00100001B ⇒ 001000011B ⇒ 0010000111 Modře je vyznačena pravá část pravidla, které bylo v daném kroku odvozenı́ použito (napřı́klad v druhém kroku jsme použili pravidlo A → 0A00). Derivačnı́ strom se vždy vztahuje ke konkrétnı́ derivaci. Podle výše uvedené derivace postupně sestrojı́me derivačnı́ strom takto (jsou uvedeny i mezikroky): S ⇒ AB S ⇒ AB ⇒ 0A00B S → AB A → 0A00 S A B S A 0 58 A 0 B 0 P 5.1 DERIVAČNÍ STROM 59 S ⇒ AB ⇒ 0A00B ⇒ 0010000B A → 0100 S ⇒ . . . ⇒ 0010000B ⇒ 00100001B B → 1B S A S A B B 0 A 0 0 0 A 0 0 0 1 0 0 0 1 0 0 S ⇒ . . . ⇒ 00100001B ⇒ 001000011B B → 1B S ⇒ . . . ⇒ 001000011B ⇒ 0010000111 B→1 S A S A B 0 A 0 0 0 1 0 0 B 1 1 B 1 B B 0 A 0 0 0 1 0 0 1 B 1 B 1 V jednotlivých krocı́ch je kromě samotného stromu uvedeno použité pravidlo a také momentálnı́ stav derivace. Modře je vyznačena větná forma, kterou jsme v daném kroku vytvořili. Můžeme si všimnout, že v derivačnı́m stromě vždy jde o obsah listů stromu v daném kroku. Derivačnı́ strom dané derivace je poslednı́ v pořadı́. Úkol 5.1 1. Podle zadané gramatiky vytvořte derivaci slova ababa a k nı́ derivačnı́ strom. G = ({S, A, B}, {a, b}, P, S) S → bAB | aBA | ε A → abAab | ε B → baBba | ε 2. Podle gramatiky z předchozı́ho přı́kladu vytvořte derivaci jakéhokoliv slova o délce alespoň 6 začı́najı́cı́ho symbolem b a k této derivaci sestrojte derivačnı́ strom. 3. Sestrojte gramatiky generujı́cı́ následujı́cı́ jazyky. V každém jazyce vyberte jakékoliv slovo o délce alespoň 3 symboly, vytvořte jeho derivaci a sestrojte k nı́ derivačnı́ strom. (a) L1 = (ab)i cj (ba)i : i, j ≥ 0 (b) L2 = {0n 11n : n ≥ 3} ∪ {ε} (c) L3 = 0i 10i : i ≥ 1 ∪ 1i 01i : i ≥ 1 L C 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 60 (d) L4 = {abc, bad, f aa, diff } (nezapomeňte, že na pravé straně pravidla může být jakýkoliv řetězec) 5.2 5.2.1 Úpravy bezkontextových gramatik Redukce gramatiky Účelem redukce gramatiky je snı́ženı́ počtu neterminálů při zachovánı́ generovaného jazyka. Odstraňujeme • nadbytečné neterminály (tj. neterminály, ze kterých nelze vygenerovat žádné terminálnı́ slovo), P • nedostupné symboly (terminálnı́ i neterminálnı́ – nelze je vygenerovat ze startovacı́ho symbolu). Zachováváme toto pořadı́ – nejdřı́v nadbytečné a pak teprve nedostupné. Použı́váme podobný postup jako při redukci množiny stavů konečného automatu. V obou přı́padech rekurzı́vně (použijeme indukci) tvořı́me množinu obsahujı́cı́ všechny symboly, které v gramatice majı́ zůstat. Formálnı́ zápis pro gramatiku G = (N, T, P, S): • Odstraňujeme nadbytečné neterminály: N0 = T (báze indukce – použijeme množinu terminálů) ∗ } Ni = Ni−1 ∪ {A ∈ N : (A → α) ∈ P, α ∈ Ni−1 (do následujı́cı́ množiny vložı́me obsah předchozı́ množiny a přidáme všechny symboly, pro které existuje pravidlo, kde na pravé straně jsou pouze symboly z množiny z předchozı́ho kroku, pozor, α může být i ε – prázdné slovo!) Končı́me tehdy, když v daném kroku nepřidáme žádný symbol, Nk+1 = Nk , kde k je nejmenšı́ takový index, pro který daná rovnost platı́. Pak zjistı́me novou množinu neterminálů: N 0 = N ∩ Nk (protože v množině Nk jsou také terminály). • Odstraňujeme nedostupné symboly (terminálnı́ i neterminálnı́): V0 = {S} (báze indukce, začı́náme startovacı́m symbolem) Vi = Vi−1 ∪ {X ∈ (N ∪ T ) : (A → αXβ) ∈ P, A ∈ Vi−1 } (přidáme všechny symboly, které se nacházejı́ na pravé straně pravidel přepisujı́cı́ch již zařazené neterminály) Končı́me opět tehdy, když nedocházı́ ke změnám, Vr+1 = Vr , kde r je nejmenšı́ takový index, pro který daná rovnost platı́. Pak zjistı́me nové množiny terminálů a neterminálů: N 00 = N 0 ∩ Vr T 0 = T ∩ Vr P 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 61 Když se nad tı́m zamyslı́me, tak při odstraňovánı́ nadbytečných neterminálů vlastně zjišt’ujeme, které symboly jsou použı́vány, proti směru derivace (mnemotechnická pomůcka: v pravidlech se dı́váme „proti směru šipky“), kdežto při odstraňovánı́ nedostupných symbolů naopak jdeme ve směru derivace a zjišt’ujeme, které symboly mohou být vygenerovány (po různém počtu kroků) ze startovacı́ho symbolu (v pravidlech postupujeme „ve směru šipky“). Přı́klad 5.2 Redukujeme následujı́cı́ gramatiku: G = ({S, A, B, C, D}, {a, b, c, d}, P, S) S → bS | bD | ε A → aCB | bAD B → dB | d C → bCC | aAbB D → bA | cDb | aS | ε Nejdřı́v odstranı́me nadbytečné neterminály. Rekurzı́vně vytvořı́me množinu všech symbolů, které jsou bud’ terminálnı́ a nebo z nich lze vygenerovat terminálnı́ řetězec (v přı́padě, že jde o neterminál). Bázı́ rekurze je množina všech terminálů. V dalšı́ch krocı́ch přidáváme neterminály z levých stran pravidel, na jejichž pravé straně jsou pouze symboly, které jsme do našı́ množiny přidali v předchozı́ch krocı́ch (a nebo pro ně existuje ε-pravidlo). N0 = {a, b, c, d} N1 = {a, b, c, d, S, B, D} N2 = {a, b, c, d, S, B, D} (podle pravidel S → ε, B → d, D → ε) (podle pravidel S → bS | bD, B → dB, D → cDb | aS) Protože jsme do množiny N2 oproti množině N1 nic dalšı́ho nepřidali, rekurze končı́. Množina neterminálů bude N ∩ N2 = {S, B, D}, vyřadı́me neterminály A a C včetně všech pravidel, která je obsahujı́ na levé nebo pravé straně. Gramatika po odstraněnı́ nadbytečných symbolů je následujı́cı́: G0 = ({S, B, D}, {a, b, c, d}, P 0 , S) S → bS | bD | ε B → dB | d D → cDb | aS | ε Zbývá odstranit nedostupné symboly. Postupujeme opět rekurzı́vně. Vytvořı́me množinu symbolů, které se nacházejı́ v nějaké větné formě v derivaci ze startovacı́ho symbolu. Začneme množinou obsahujı́cı́ pouze startovacı́ symbol, v každém kroku přidáváme symboly, které se nacházejı́ na pravých stranách pravidel přepisujı́cı́ch symboly zařazené v předchozı́ch krocı́ch. V0 = {S} V1 = {S, b, D} (podle pravidel S → bS | bD) L 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK V2 = {S, b, D, c, a} V3 = V2 62 (podle pravidel D → cDb | aS) Z postupu vyplývá, že pokud v některém kroku přidáme pouze terminálnı́ symboly, v následujı́cı́m kroku již nelze nic přidat (protože terminály se nepřepisujı́ žádným pravidlem) a rekurze končı́. Je zřejmé, že nedostupný neterminál je jeden (B) a terminál taktéž jeden (d). Redukovaná gramatika (tj. již bez nadbytečných i nedostupných symbolů) je následujı́cı́: G00 = ({S, D}, {a, b, c}, P 00 , S) S → bS | bD | ε D → cDb | aS | ε Úkol 5.2 C Redukujte tyto gramatiky: G1 = ({S, A, B, C, D}, {a, b, c, d}, P, S) G2 = ({S, A, B, C, D, E}, {0, 1, 2, 3}, P, S) S → aBa | bA | c | ABc A → aA | dD | bDa B → aB | bA | bS C → aSc | cC | c D → dD | aAb S → AB0 | 2E | A1 | ε A → 0A0 | 1B | 1S B → 01B | 1D | EB C → 2A | 1S | 0 D → D1 | 3B E → 0E1 | 1B G3 = ({S, A, B, C, D}, {a, b, c}, P, S) G4 = ({S, A, B, C, D}, {a, b, c}, P, S) S → aD | bBD | c A → bbB | aA B → bB | aAb C → aD | b D → cS | aDD | ε 5.2.2 S → BaD | a A → aS | cc B → bSB | DC | ε C → aSDC | bSC D → aC | Da | BS Převod na nezkracujı́cı́ bezkontextovou gramatiku Nezkracujı́cı́ bezkontextová gramatika bud’ vůbec neobsahuje ε-pravidla (pokud v jazyce generovaném gramatikou nenı́ prázdné slovo), a nebo existuje jediné takové pravidlo, a to pro startovacı́ symbol gramatiky, pak ale se startovacı́ symbol nesmı́ vyskytovat na pravé straně žádného pravidla (to znamená, že v každé derivaci se vyskytuje pouze na jejı́m začátku, pak už ne). P 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 63 Při převodu na nezkracujı́cı́ gramatiku odstraňujeme ε-pravidla tak, že „simulujeme“ jejich použitı́. Stejný postup jsme použili i v algoritmu převodu regulárnı́ gramatiky do normálnı́ formy. Přı́klad 5.3 K následujı́cı́ gramatice vytvořı́me ekvivalentnı́ gramatiku s nezkracujı́cı́mi pravidly. G = ({S, A, B}, {a, b}, P, S) S → aaB | bAb A → aBa | ε B → AbAa | a Téměř všechna pravidla jsou nezkracujı́cı́, je zde pouze jediné ε-pravidlo – A → ε. Přepisovaný symbol A se nacházı́ na pravé straně dvou pravidel – druhého a pátého. Obě tato pravidla necháme a přidáme jejich varianty se „simulovaným“ použitı́m ε-pravidla: G0 = ({S, A, B}, {a, b}, P 0 , S) S → aaB | bAb | bb A → aBa B → AbAa | bAa | Aba | ba | a U prvnı́ho ze zpracovávaných pravidel máme jen jeden výskyt neterminálu A, tedy existuje pouze jediná dalšı́ varianta (toto jediné A odstranı́me – přepı́šeme na ε). U druhého zpracovávaného pravidla jsou dva výskyty neterminálu A, proto musı́me vytvořit variant vı́ce – odstranı́me jen prvnı́ A, odstranı́me jen druhé, a nebo odstranı́me obě. Derivace v gramatice G0 bude téměř stejná jako v gramatice G, ale kroky s použitı́m ε-pravidel jsou „přeskočeny“, napřı́klad v gramatice G: v gramatice G0 : S ⇒ aaB ⇒ aaAbAa ⇒ aabAa ⇒ aabaBaa ⇒ aabaaaa S ⇒ aaB ⇒ aabAa ⇒ aabaBaa ⇒ aabaaaa Přı́klad 5.4 K zadané gramatice vytvořı́me ekvivalentnı́ nezkracujı́cı́ gramatiku (tj. generujı́cı́ tentýž jazyk). G = ({S, A}, {a, b, c}, P, S) S → aaS | bbA | ε A → cSbSc | ε Gramatika generuje také prázdné slovo. Proto nejdřı́v použijeme stejný postup jako v předchozı́m přı́kladu, ale pak ještě musı́me zajistit, aby výsledná gramatika také generovala prázdné slovo a přitom zůstala nezkracujı́cı́. Nejdřı́v vytvořı́me pomocnou gramatiku G0 , která nemá žádná ε-pravidla, jejı́ jazyk je L(G0 ) = L(G) − {ε}. 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 64 G0 = ({S, A}, {a, b, c}, P 0 , S) S → aaS | aa | bbA | bb A → cSbSc | cbSc | cSbc | cbc Přidáme nový neterminál (startovacı́ symbol) a vytvořı́me gramatiku, která již bude ekvivalentnı́ původnı́ gramatice. G00 = ({S 0 , S, A}, {a, b, c}, P 00 , S 0 ) S0 → S | ε S → aaS| aa | bbA| bb A → cSbSc | cbSc | cSbc | cbc Derivace v gramatice G: Derivace v gramatice G00 : S ⇒ aaS ⇒ aaaaS ⇒ aaaa S 0 ⇒ S ⇒ aaS ⇒ aaaa Přı́klad 5.5 Někdy se může stát, že „simulacı́“ ε-pravidla vytvořı́me dalšı́ ε-pravidlo. Pak je třeba postup provádět rekurzı́vně tak dlouho, dokud nejsou všechna ε-pravidla odstraněna. G = ({S, A, B}, {a, b}, P, S) S → bA | aS | bB | a A → baB | ε B → AA | b Po odstraněnı́ pravidla A → ε dostaneme tuto gramatiku: G0 = ({S, A, B}, {a, b}, P 0 , S) S → bA | b | aS | bB | a A → baB B → AA | A | ε | b Odstraněnı́m obou symbolů A v pravidle B → AA vzniklo dalšı́ ε-pravidlo, které je také třeba odstranit: G00 = ({S, A, B}, {a, b}, P 00 , S) S → bA | b | aS | bB | b | a A → baB | ba B → AA | A | b Rekurze při řešenı́ pravidel se zbavı́me tak, že ji přeneseme jinam. Můžeme použı́t řešenı́ podobné tomu z redukce gramatiky – vytvořı́me (rekurzı́vně) množinu Nε všech neterminálů, které lze (třeba i po vı́ce než jednom kroku) přepsat na prázdné slovo ε. V množině Nε,0 bude pouze prázdný řetězec, tj. Nε,0 = {ε}. V dalšı́ch krocı́ch do této množiny přidáváme neterminály, které lze přepsat na řetězec skládajı́cı́ se pouze ze P 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 65 symbolů, které byly do této množiny přidány v předchozı́ch krocı́ch, tj. Nε,i = Nε,i−1 ∪ {X ∈ N : X → α, α ∈ Nε,i−1 }. Přı́klad 5.6 Podle pravidel gramatiky G = ({S, A, B, C, D}, {a, b, c, d}, P, S) S → aAa | aa A → BC | b B → aC | cD | ε C → AAa | ε D → AAB | d vytvořı́me tyto množiny: Nε,0 Nε,1 Nε,2 Nε,3 = {ε} = {B, C} podle B → ε, C → ε, prázdné slovo už nezařazujeme = {B, C, A} podle A → BC = {B, C, A, D} podle D → AAB V dalšı́m kroku již nelze dalšı́ neterminál přidat (ostatně všechny už v množině jsou), proto Nε = Nε,3 = {B, C, A, D}. Množiny použijeme takto: pokud je na pravé straně pravidla některý z neterminálů obsažených v množině Nε , pak vytvořı́me dalšı́ pravidla se „simulovaným“ použitı́m εpravidel na tento neterminál, ε-pravidla odstranı́me. Postup je prakticky stejný jako předchozı́, ale zpracovánı́ gramatiky již nenı́ třeba provádět rekurzı́vně (rekurze byla použita při generovánı́ množiny Nε ). Fialovou barvou jsou zvýrazněny neterminály obsažené v množině Nε (a tedy vytvořı́me pravidlo, ve kterém je odstranı́me), modrou barvou pak nová pravidla. S → aAa | aa | aa (vlastně máme dvě stejná pravidla, jedno může být odstraněno) A → BC | C | B | b B → aC | a | cD | c C → AAa | Aa | a D → AAB | AB | B | d Přı́klad 5.7 Postup předvedený v předchozı́m přı́kladu použijeme na gramatiku z přı́kladu 5.5. G = ({S, A, B}, {a, b}, P, S) S → bA | aS | bB | a A → baB | ε B → AA | b Opět rekurzı́vně vytvořı́me množinu Nε : 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 66 Nε,0 = {ε} Nε,1 = {A} Nε,2 = {A, B} = Nε,3 = Nε Vycházı́ nám tato gramatika: G0 = ({S, A, B}, {a, b}, P 0 , S) S → bA | b | aS | bB | b | a A → baB | ba B → AA | A | b Oproti původnı́mu řešenı́ nenı́ třeba několikrát přepisovat pravidla gramatiky. Úkol 5.3 K následujı́cı́m gramatikám vytvořte ekvivalentnı́ nezkracujı́cı́ gramatiky. G1 = ({S, A, B}, {a, b}, P, S) G2 = ({S, A}, {0, 1}, P, S) G3 = ({S, A, B}, {a, b}, P, S) G4 = {S, A, B}, {a, b}, P, S) G5 = ({S, A, B}, {a, b}, P, S) G6 = ({S, A, B}, {a, b}, P, S) S → aSbA | ab A → aAbA | ε B → bAbB | b S → BAa | aS | ε A → aA | ε B → bBA | ε S → aABa | bS | b A → aAA | bB | ε B → bB | aS | b 5.2.3 C S → S1S1 | A | ε A → 0A0 | 1 S → AB | BA | b A → aaA | ε B → AA | bS | a S → aAA | bB | ε A → AaB | a | ε B → BbAA | a Odstraněnı́ jednoduchých pravidel Jednoduchá pravidla jsou taková pravidla, na jejichž pravé straně máme jen jediný symbol, a to neterminál, napřı́klad A → B (opět jsme se s nimi setkali při převodu regulárnı́ gramatiky na normálnı́ formu). Jednoduchá pravidla zbytečně prodlužujı́ výpočet a v některých algoritmech mohou být překážkou při programovánı́. Pokud tento typ pravidel chceme odstranit, můžeme jednoduše nahradit symbol na pravé straně jednoduchého pravidla celou množinou pravidel, na která se tento symbol P 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 67 přepisuje. Napřı́klad pokud existujı́ pravidla B → α | β | γ, pak jednoduché pravidlo A → B nahradı́me pravidly A → α | β | γ (v derivaci to bude znamenat zkrácenı́ odvozenı́ o jeden krok – zpracovánı́ jednoduchého pravidla). Protože touto náhradou můžeme opět pro daný neterminál dostat jednoduché pravidlo, postup je rekurzivnı́ a lze ho zjednodušit přenesenı́m rekurze na množiny neterminálů podobně jako v předchozı́ch postupech. Vytvářı́me množiny NA postupně pro všechny neterminály A ∈ N , které inicializujeme jednoprvkovou množinou obsahujı́cı́ neterminál v označenı́ množiny (tj. v tomto přı́padě A). Jde o množiny neterminálů, jejichž pravidla se pomocı́ jednoduchých pravidel majı́ postupně připsat do množiny pravidel pro daný neterminál A. Přı́klad 5.8 Odstranı́me jednoduchá pravidla v následujı́cı́ gramatice: G = ({S, A, B, C}, {a, b}, P, S) S → aBa | A A → aA | B B → bB | AB | C | ε C → bA | b Rekurzı́vně vytvořı́me množiny NS , NA , NB , NC . Na začátku rekurze, v bázi, bude v dané množině pouze ten symbol, pro který množinu tvořı́me, tj. napřı́klad NS,0 = {S}. NS,0 NS,1 NS,2 NS,3 = {S} = {S, A} (podle S → A) = {S, A, B} (podle A → B) = {S, A, B, C} = NS,4 = NS (podle B → C) NA,0 = {A} NA,1 = {A, B} (podle A → B NA,2 = {A, B, C} = NA,3 = NA NB,0 = {B} NB,1 = {B, C} = NB,2 = NA (podle B → C) (podle B → C) NC,0 = {C} = NC,1 = NC Z gramatiky odstranı́me všechna jednoduchá pravidla. Po jejich odstraněnı́ pak použijeme vytvořené množiny – pokud napřı́klad pro neterminál A je NA = {A, B, C}, pak v gramatice pro neterminál A použijeme všechna pravidla, která v původnı́ gramatice byla pro neterminály A, B, C. G0 = ({S, A, B, C}, {a, b}, P 0 , S) S → aBa | aA | bB | AB | ε | bA | b A → aA | bB | AB | ε | bA | b B → bB | AB | ε | bA | b C → bA | b P 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 68 Pokud nám vadı́ pouze jednoduchá pravidla samotná, předchozı́ postup je dostačujı́cı́. Jestliže však je překážkou samotné chovánı́ jednoduchých pravidel (napřı́klad prodlužovánı́ derivace), je třeba předem odstranit ε-pravidla. Napřı́klad v gramatice z přı́kladu 5.9 po odstraněnı́ jednoduchých pravidel existuje derivace S ⇒ AB ⇒ A ⇒ . . ., kde nacházı́me ekvivalent použitı́ jednoduchého pravidla S → A z původnı́ gramatiky. L Přı́klad 5.9 Gramatiku z přı́kladu 5.9 převedeme na nezkracujı́cı́, pak odstranı́me jednoduchá pravidla. G = ({S, A, B, C}, {a, b}, P, S) S → aBa | A A → aA | B B → bB | AB | C | ε C → bA | b Po převodu na nezkracujı́cı́ gramatiku: Odstranı́me jednoduchá pravidla: Nε = {B, A, S} NS = {S, A, B, C} NA = {A, B, C} NB = {B, A, C} (všimněte si rozdı́lu oproti přı́kladu 5.9) NC = {C} G0 = ({S, A, B, C}, {a, b}, P 0 , S) S → aBa | aa | A | ε A → aA | a | B B → bB | b | AB | A | B | C C → bA | b G00 = ({S, A, B, C}, {a, b}, P 00 , S) S → aBa | aa | aA | a | ε | aA | a | bB | b | AB | bA | b A → aA | a | bB | b | AB | bA | b B → bB | b | AB | aA | a | bA | b C → bA | b Úkol 5.4 Odstraňte jednoduchá pravidla z následujı́cı́ch gramatik: G1 = ({S, A, B}, {0, 1}, P, S) S → 0A1 | B | ε A → 1S | S | B B → 1A | S | 0 G2 = ({S, A, B}, {a, b}, P, S) S → aA | bB | A A → bAb | S | ε B → aBa | A | a C 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 5.2.4 69 Gramatika bez cyklu a vlastnı́ gramatika Gramatika bez cyklu je taková gramatika, kde neexistuje žádná derivace A ⇒+ A pro jakýkoliv neterminál A. Už z definice je zřejmé, jak gramatiku upravit, aby splňovala tuto vlastnost – stačı́ ji převést na nezkracujı́cı́ gramatiku (bez ε-pravidel) a odstranit jednoduchá pravidla. Vlastnı́ gramatika je gramatika bez cyklu, nezkracujı́cı́ a bez nadbytečných symbolů. Postup je tedy následujı́cı́: • převedeme gramatiku na nezkracujı́cı́, P P • odstranı́me jednoduchá pravidla, • odstranı́me nadbytečné symboly postupem, který již známe z redukce gramatiky. 5.2.5 Levá a pravá rekurze Gramatika je rekurzivnı́ zleva, pokud v nı́ existuje derivace A ⇒+ Aα, gramatika je rekurzı́vnı́ zprava, pokud v nı́ existuje derivace A ⇒+ αA, kde A je některý neterminál gramatiky, α je jakýkoliv řetězec z terminálů a neterminálů. Levá nebo pravá rekurze nám může bránit v některých dalšı́ch úpravách a nebo v naprogramovánı́ postupu popsaného gramatikou (ano, čtete dobře, pı́še se tady o programovánı́ podle gramatiky, bližšı́ informace v předmětu Překladače). Obvykle nám vadı́ jen levá a nebo jen pravá rekurze, proto jejich odstraněnı́ řešı́me jednoduše převodem na tu, která nám nevadı́. Při odstraňovánı́ rekurze vycházı́me z toho, že k témuž řetězci lze dojı́t vı́ce různými způsoby. Postup je následujı́cı́: 1. Předběžné úpravy: převedeme gramatiku do tvaru vlastnı́ gramatiky, tj. • převedeme do tvaru nezkracujı́cı́ gramatiky, • odstranı́me jednoduchá pravidla, • odstranı́me nadbytečné a nedostupné symboly. 2. Levou rekurzi převedeme na pravou rekurzi. V druhém bodu vycházı́me z této myšlenky: předpokládejme, že máme sadu pravidel pro tentýž neterminál, označme A → Aα1 | Aα2 | . . . Aαn | β1 | β2 | . . . | βk kde řetězce βi , 1 ≤ i ≤ k začı́najı́ jiným symbolem než A Je zřejmé, že tato pravidla v rekurzi vygenerujı́ řetězec, na jehož začátku bude některý z podřetězců βi a bude následovat libovolný počet podřetězců αj , napřı́klad: A ⇒ Aα3 ⇒ Aα5 α3 ⇒ Aα2 α5 α3 ⇒ β8 α2 α5 α3 Je třeba tedy sestavit pravidla taková, aby byly generovány tytéž řetězce, ale bez levé rekurze. Původnı́ sadu pravidel nahradı́me jinou sadou: P P 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 70 A → β1 B | β2 B | . . . | βk B | β1 | β2 | . . . | βk B → α1 B | α2 B | . . . | αn B | α1 | α2 | . . . | αn Předchozı́ derivaci podle původnı́ch pravidel by odpovı́dala následujı́cı́ derivace: A ⇒ β8 B ⇒ β8 α2 B ⇒ β8 α2 α5 B ⇒ β8 α2 α5 α3 Vidı́me, že je generován tentýž řetězec, jen v opačném pořadı́ – zleva mı́sto zprava, tedy levou rekurzi jsme nahradili pravou rekurzı́. Pozor – provedenı́m výše naznačených úprav můžeme opět zı́skat levě rekurzivnı́ pravidla, tedy postup je třeba provádět tak dlouho, dokud se nezbavı́me levě rekurzivnı́ch pravidel. Přı́klad 5.10 Následujı́cı́ gramatiku zbavı́me levé rekurze. Gramatika je nezkracujı́cı́, neobsahuje jednoduchá pravidla, neobsahuje ani nadbytečné a nedostupné symboly, můžeme tedy přı́mo přikročit k odstraňovánı́ levě rekurzivnı́ch pravidel. G = ({S, A, B}, {a, b, c}, P, S) S → aAb | b A → Aa | bB | ab B → Ba | Bb | ca Levá rekurze je v pravidlech A → Aa a dále B → Ba | Bb. Nejdřı́v vyřešı́me prvnı́ z těchto pravidel – pro neterminál A. Množinu pravidel A → Aa | bB | ab nahradı́me jinou množinou, která generuje tentýž řetězec. Jak může vypadat derivace z neterminálu A? A ⇒ Aa ⇒ Aaa ⇒ Aaaa ⇒∗ Aai ⇒ bBai (generujeme zprava doleva, a to nejdřı́v rekurzı́vnı́m pravidlem, rekurze je pak ukončena vlevo některým nerekurzı́vnı́m pravidlem). Stejný řetězec lze generovat pravou rekurzı́, a to přidánı́m nového neterminálu a úpravou pravidel (původnı́ pravidla jsou A → Aa | bB | ab): A → bBA0 | abA0 | bB | ab A0 → aA0 | a Derivace ze symbolu A pak vypadá následovně: A ⇒ bBA0 ⇒ bBaA0 ⇒ bBaaA0 ⇒∗ bBai−1 A0 ⇒ bBai Pravidla B → Ba | Bb | ca nahradı́me pravidly B → caB 0 | ca B 0 → aB 0 | bB 0 | a | b Vytvořili jsme tedy ekvivalentnı́ gramatiku bez přı́mé levé rekurze: G0 = ({S, A, A0 , B, B 0 }, {a, b, c}, P 0 , S) S → aAb | b A → bBA0 | abA0 | bB | ab A0 → aA0 | a B → caB 0 | ca B 0 → aB 0 | bB 0 | a | b L 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 71 Uvedený postup je použitelný pouze na vlastnı́ gramatiku (tj. gramatiku bez cyklu, nezkracujı́cı́, bez nadbytečných symbolů). Gramatika z předchozı́ho přı́kladu je vlastnı́, proto nebylo třeba ji předem upravovat. Přı́klad 5.11 Odstranı́me levou rekurzi v následujı́cı́ gramatice: G = ({S, A, B}, {a, b, c}, P, S) S → bAa | c | A A → Ba | b | Abb B → aBb | Bbc | ca | ε Tato gramatika nenı́ vlastnı́. Je třeba nejdřı́v převést ji na nezkracujı́cı́, odstranit jednoduchá pravidla a teprve potom můžeme odstranit levou rekurzi. 1. Odstranı́me ε-pravidla (resp. jediné, B → ε): G0 = ({S, A, B}, {a, b, c}, P 0 , S) S → bAa | c | A A → Ba | a | b | Abb B → aBb | ab | Bbc | bc | ca 2. Odstranı́me jednoduché pravidlo S → A: G00 = ({S, A, B}, {a, b, c}, P 00 , S) S → bAa | c | Ba | a | b | Abb A → Ba | a | b | Abb B → aBb | ab | Bbc | bc | ca 3. Odstranı́me přı́mou levou rekurzi. Pravidla A → Ba | a | b | Abb nahradı́me pravidly A → BaA0 | aA0 | bA0 | Ba | a | b A0 → bbA0 | bb Dále pravidla B → aBb | ab | Bbc | bc | ca nahradı́me pravidly B → aBbB 0 | abB 0 | aB 0 | |bcB 0 | caB 0 | aBb | ab | bc | ca B 0 → bcB 0 | bc Vytvořili jsme tuto gramatiku: G000 = ({S, A, A0 , B, B 0 }, {a, b, c}, P 000 , S) S → bAa | c | Ba | a | b | Abb A → BaA0 | aA0 | bA0 | Ba | a | b A0 → bbA0 | bb B → aBbB 0 | abB 0 | aB 0 | |bcB 0 | caB 0 | aBb | ab | bc | ca B 0 → bcB 0 | bc L 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 72 Přı́klad 5.12 Odstranı́me pravou rekurzi v této gramatice: G = ({S, A, B}, {a, b, c}, P, S) S → aAb | AB A → abA | c | Sc B → bbB | cB | a Jde o vlastnı́ gramatiku, nemusı́me ji předem do tohoto tvaru upravovat. Postupujeme stejně jako u odstraněnı́ levé rekurze, jen zaměnı́me levou a pravou stranu. Přidáme nové neterminály a upravı́me pravidla. Z neterminálu A může být napřı́klad tato derivace: A ⇒ abA ⇒ ababA ⇒∗ (ab)i A ⇒ (ab)i c Pravidla A → abA | c | Sc nahradı́me pravidly A → A0 c | A0 Sc | c | Sc A0 → A0 ab | ab Derivace z neterminálu A se změnı́: A ⇒ A0 c ⇒ A0 abc ⇒ A0 ababc ⇒∗ A0 (ab)i−1 c ⇒ (ab)i c Vygenerovaný řetězec je tentýž, ale zatı́mco u pravé rekurze byl generován zleva doprava, u levé rekurze je generován opačně – zprava doleva. Podobně upravı́me pravidla B → bbB | cB | a: B → B0a | a B 0 → B 0 bb | B 0 c | bb | c Vycházı́ nám tato gramatika: G0 = ({S, A, A0 , B, B 0 }, {a, b, c}, P 0 , S) S → aAb | AB A → A0 c | A0 Sc | c | Sc A0 → A0 ab | ab B → B0a | a B 0 → B 0 bb | B 0 c | bb | c Rekurze může být také nepřı́má, napřı́klad v pravidlech A → Bba | a B → Aab | b Levou rekurzi, včetně nepřı́mé, odstranı́me tak, že pravidla převedeme do tvaru, kdy pravá strana pravidla začı́ná neterminálem pouze za přesně určených okolnostı́. Postup: 1. Stanovı́me pořadı́ neterminálů. Můžeme si je napřı́klad označit indexy, přejmenovat nebo jednoduše určit jejich pořadı́. Necht’ pořadı́ neterminálů je (A1 , A2 , . . . , An ). 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 73 Účelem algoritmu je upravit pravidla tak, aby mohla začı́nat pouze neterminály s vyššı́m indexem než je index přepisovaného neterminálu. Tı́m zcela odstranı́me levou rekurzi. 2. Pro neterminály Ai , Aj , 1 ≤ i, j ≤ n postupně transformujeme pravidla. Pro prvnı́ krok stanovı́me i = 1, j = 1. • Pokud j < i a existuje pravidlo Ai → Aj α, kde α je jakýkoliv řetězec (tj. pravidlo pro Ai začı́ná neterminálem Aj ), pak symbol Aj v pravidle nahradı́me postupně všemi pravými stranami pravidel pro Aj , napřı́klad Ai → Aj abB Aj → bDAi a | CAj a Prvnı́ z uvedenených pravidel nahradı́me pravidly Ai → bdAi aabB | CAj aabB. Provedeme j = j + 1. • Pokud j = i, pak odstranı́me přı́mou levou rekurzi podle postupu, který už známe. Provedeme j = j + 1. • Pokud j > i, provedeme i = i + 1, j = 1. 3. Bod 2 postupu provedeme pro všechna i, 1 ≤ i ≤ n. Aby byl postup použitelný, musı́ být uplatněn pouze na vlastnı́ gramatiku. Postup pro pravou rekurzi je obdobný, jen zaměnı́me levou za pravou stranu. Přı́klad 5.13 Odstranı́me levou rekurzi včetně nepřı́mé v následujı́cı́ gramatice: G = ({S, A, B}, {a, b, c}, P, S) S → aA | AB | b A → SBa | a B → Bb | Aa | c Přı́má levá rekurze je zde jen v jednom pravidle, ale nepřı́má rekurze se týká vı́ce pravidel. Gramatika je vlastnı́, nenı́ třeba ji do tohoto tvaru upravovat. Stanovı́me pořadı́ neterminálů: (A1 , A2 , A3 ) = (S, A, B). • i = 1, j = 1 : nenı́ třeba upravovat, pravidla pro S neobsahujı́ přı́mou rekurzi. • i = 2, j = 1 : jedno z pravidel pro A začı́ná neterminálem s „nižšı́m indexem“, S; upravı́me: A → aABa | ABBa | bBa | a • i = 2, j = 2 : řešı́me přı́mou rekurzi (protože i = j): A → aABaA0 | bBaA0 | aA0 | aABa | bBa | a A0 → BBaA0 | BBa 5.2 ÚPRAVY BEZKONTEXTOVÝCH GRAMATIK 74 • i = 3, j = 1 : žádné z pravidel pro B nezačı́ná symbolem S • i = 3, j = 2 : změna se týká druhého pravidla pro symbol B (začı́ná symbolem A, jehož index je 2). Při nahrazenı́ symbolu A v tomto pravidle použijeme již upravená pravidla pro A: B → Bb | aABaA0 a | bBaA0 a | aA0 a | aABaa | bBaa | aa | c • i = 3, j = 3 : odstranı́me přı́mou rekurzi: B → aABaA0 aB 0 | bBaA0 aB 0 | aA0 aB 0 | aABaaB 0 | bBaaB 0 | aaB 0 | cB 0 | aABaA0 a | bBaA0 a | aA0 a | aABaa | bBaa | aa | c B 0 → bB 0 | b Celá gramatika bez levé rekurze: G0 = ({S, A, A0 , B, B 0 }, {a, b, c}, P 0 , S) S → aA | AB | b A → aABaA0 | bBaA0 | aA0 | aABa | bBa | a A0 → BBaA0 | BBa B → aABaA0 aB 0 | bBaA0 aB 0 | aA0 aB 0 | aABaaB 0 | bBaaB 0 | aaB 0 | cB 0 | aABaA0 a | bBaA0 a | aA0 a | aABaa | bBaa | aa | c B 0 → bB 0 | b Odstraněnı́m přı́mé rekurze rozšiřujeme množinu neterminálů, proto je nutné dynamicky upravovat také nadefinovanou posloupnost neterminálů určujı́cı́ pořadı́. Neterminály s pravidly, které takto vytvořı́me, je třeba zahrnout do algoritmu. Přı́klad 5.14 Odstranı́me levou rekurzi v následujı́cı́ gramatice: G = ({S, A, B}, {a, b}, P, S) S → aA | bB A → BaA | ab B → BaA | b • i = 1, j = 1 : nenı́ třeba upravovat, pravidla pro S neobsahujı́ přı́mou rekurzi. • i = 2, j = 1, 2 : nenı́ třeba upravovat, pravidla pro A nezačı́najı́ symboly S a A. • i = 3, j = 1, 2 : nenı́ třeba upravovat, pravidla pro B nezačı́najı́ symboly S a A. • i = 3, j = 3 : odstranı́me přı́mou rekurzi. B → bB 0 | b B 0 → AaB 0 | Aa Měnı́me posloupnost: (A1 , A2 , A3 , A4 ) = (S, A, B, B 0 ) 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK 75 • i = 4, j = 1 : nenı́ třeba upravovat, pravidla pro B 0 nezačı́najı́ symbolem S. • i = 4, j = 2 : B 0 → BaAaB 0 | abaB 0 | BaAa | aba • i = 4, j = 3 : B 0 → bB 0 aAaB 0 | baAaB 0 | abaB 0 | bB 0 aAa | baAa | aba • i = 4, j = 4 : nenı́ třeba upravovat. Výsledná gramatika: G0 = ({S, A, B, B 0 }, {a, b}, P 0 , S) S → aA | bB A → BaA | ab B → bB 0 | b B 0 → AaB 0 | Aa B 0 → bB 0 aAaB 0 | baAaB 0 | abaB 0 | bB 0 aAa | baAa | aba Úkol 5.5 1. Odstraňte levou rekurzi v těchto gramatikách (zvažte, zda nenı́ třeba gramatiku předem upravit): G1 = ({S, A, B}, {a, b, c}, P, S) G2 = ({S, A, B}, {a, b}, P, S) S → aAB | b | SB | ε A → bA | Aac | AB | a B → BbA | c S → AB | BA A → ABbA | bA | ε B → SA | a | BbA G3 = ({S, A, B}, {a, b}, P, S) G4 = ({E, T, F }, {i, +, ∗, (, )}, P, S) S → AB | a A → AaB | Sb | ba B → BB | b | Ab | ε E →E+T |T T →T ∗F |F F → (E ) | i 2. Odstraňte pravou rekurzi v těchto gramatikách (přı́p. gramatiku předem upravte): G1 = ({S, A, B}, {0, 1}, P, S) G2 = ({S, A, B}, {a, b}, P, S) S → 11A | 01B | ε A → 11A | B01 | 10 B → 01B | 00AB | 1A1 | 01 S → aAb | abB | ASA A → bA | B | ε B → bbB | abAB | abb C 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK 5.3 76 Normálnı́ formy bezkontextových gramatik Normálnı́ formy (čehokoliv) sloužı́ k usnadněnı́ porovnávánı́ a přı́padných dalšı́ch úprav, normované gramatiky se také využı́vajı́ pro zjednodušenı́ některých důkazů. V přı́padě bezkontextových gramatik lze využı́t Chomského normálnı́ formu (CNF) a Greibachové normálnı́ formu (GNF). 5.3.1 Chomského normálnı́ forma Gramatika G = (N, T, P, S) v Chomského normálnı́ formě (značı́me CNF) má všechna pravidla v jednom z následujı́cı́ch tvarů: • A → BC, tedy na pravé straně dva neterminály, P • A → a, tedy na pravé straně jeden terminál, a nebo • S → ε – pouze v přı́padě, že S se nevyskytuje na pravé straně žádného pravidla (tj. toto pravidlo lze využı́t pouze na začátku derivace). Postupujeme takto: 1. Převedeme gramatiku do tvaru vlastnı́ gramatiky (tj. převod na nezkracujı́cı́ gramatiku, odstranı́me jednoduchá pravidla, odstranı́me nadbytečné neterminály). 2. Pravidla, která majı́ na pravé straně jen jeden symbol (půjde vždy o terminál, protože jednoduchá pravidla jsme již odstranili) necháme – odpovı́dajı́ požadavkům na normálnı́ formu; dále zpracováváme jen pravidla, jejichž pravá strana má délku alespoň 2. 3. Pro každé pravidlo A → α, kde |α| > 1, provedeme tyto úpravy: • každý terminál a v řetězci α nahradı́me novým (nově vytvořeným) neterminálem Na (podle dolnı́ho indexu poznáme, který terminál byl nahrazen), takže napřı́klad pravidlo A → BaAbca zaměnı́me za pravidlo A → BNa ANb Nc Na , • vytvořı́me nová pravidla Na → a pro každý terminál a. 4. Délku pravých stran všech pravidel omezı́me na nejvýše 2 takto: pro každé pravidlo A → B1 B2 B3 . . . Bn , n > 2, vytvořı́me množinu pravidel A → B1 X1 X1 → B2 X2 ... Xn−3 → Xn−2 Bn−2 Xn−2 → Bn−1 Bn Napřı́klad pravidlo A → BCDEF nahradı́me množinou pravidel A → BX1 X1 → CX2 X2 → DX3 X3 → EF P 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK 77 Neterminály X1 , . . . , Xn−2 , které použı́váme při propojenı́ generovánı́ pravé strany původnı́ho pravidla, musı́ být nové, tedy různé pro různá pravidla, která takto upravujeme. Přı́klad 5.15 Následujı́cı́ gramatiku převedeme do Chomského normálnı́ formy: G = ({S, A, B}, {a, b, c}, P, S) S → aBAc | c A → Ab | ε B → bB | AS | a Tato gramatika nenı́ vlastnı́. Nejdřı́v odstranı́me ε-pravidlo A → ε. S → aBAc | aBc | c A → Ab | b B → bB | AS | S | a Jedno z pravidel je jednoduché (B → S), to je třeba odstranit: S → aBAc | aBc | c A → Ab | b B → bB | AS | aBAc | aBc | c | a Všechna pravidla, jejichž pravá strana je dlouhá alespoň 2 symboly, upravı́me – terminály nahradı́me přı́slušnými neterminály: S → Na BANc | Na BNc | c A → ANb | b B → Nb B | AS | Na BANc | Na BNc | c | a Na → a Nb → b Nc → c Všechna pravidla s pravou stranou delšı́ než 2 zkrátı́me a zı́skáme tuto gramatiku: GCN F = ({S, A, B, Na , Nb , Nc , X1 , X2 , X3 , X4 , X5 , X6 }, {a, b, c}, P 0 , S) S → Na X1 | Na X3 | c X1 → BX2 X2 → ANc X3 → BNc A → ANb | b B → Nb B | AS | Na X4 | Na X6 | c | a X4 → BX5 X5 → ANc X6 → BNc Jedna z derivacı́ v původnı́ gramatice G: S ⇒ aBAc ⇒ aaAc ⇒ aaAbc ⇒ aabc Derivace stejného slova v gramatice G00 (po předběžné úpravě): S ⇒ aBAc ⇒ aaAc ⇒ aabc Obdobně ve výsledné gramatice GCN F : S ⇒ Na X1 ⇒ aX1 ⇒ aBX2 ⇒ aBANc ⇒ aBAc ⇒ aaAc ⇒ aabc Na → a Nb → b Nc → c 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK Úkol 78 5.6 Následujı́cı́ gramatiky převed’te do Chomského normálnı́ formy: G1 = ({S, A, B}, {a, b}, P, S) G2 = ({S, A, B}, {0, 1}, P, S) S → AaB | ba | aA A → aAB | aAb | ab B → bBASb | b S → 0B | 1A | ε A → 10A1 | AB | ε B → A1B01 | 1S1 | 0 G3 = ({E, F, G}, {a, b}, P, E) G4 = ({S, A, B}, {0, 1}, P, S) E → aaF | GF | b F → aF aa | ε G → aF GbG | ε S → 01AB0 | 10BA1 | ε A → 1AB1AB1 | 111 B → 0B | 0 G5 = ({S, A, B}, {a, b}, P, S) G6 = ({S, A, B}, {0, 1}, P, S) S → aAbB | ε A → aA | a | Bba B → b | aSb | A 5.3.2 C S → 0A1 | 1B0 | ε A → 00AB | 1 B → 1S | 0 Greibachové normálnı́ forma Gramatika je v Greibachové normálnı́ formě (značı́me GNF), pokud všechna pravidla této gramatiky jsou v jednom z těchto tvarů: • A → aB1 B2 . . . Bn , n ≥ 0, tedy na pravé straně je vždy jeden terminál a následujı́ pouze neterminály (nemusı́ být žádný neterminál), P • S → ε (S je startovacı́ symbol gramatiky), a to pouze v přı́padě, že S se nevyskytuje na pravé straně žádného pravidla (tj. toto pravidlo lze využı́t pouze na začátku derivace). Podobně jako u převodu na Chomského normálnı́ tvar, i zde budeme gramatiku předem upravovat. Možnost existence ε-pravidla pouze pro startovacı́ symbol znamená nutnost převést gramatiku na nezkracujı́cı́. Dále odstranı́me jednoduchá pravidla a přı́padně také nadbytečné symboly. Kromě toho je zde požadavek na terminálnı́ symbol na začátku pravidla. Pokud pravidlo začı́ná neterminálem, logickým postupem by bylo dosazovat za tento neterminál postupně všechna pravidla, na který lze neterminál přepsat, a to rekurzı́vně tak dlouho, dokud nezı́skáme pravidlo začı́najı́cı́ terminálem. Pokud je však v gramatice levá rekurze, dosazovali bychom do nekonečna, proto je třeba předem odstranit levou rekurzi. Celý postup je následujı́cı́: 1. Převedeme gramatiku do tvaru vlastnı́ gramatiky (převod na nezkracujı́cı́, odstraněnı́ jednoduchých pravidel, odstraněnı́ nadbytečných symbolů). P 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK 79 2. Odstranı́me levou rekurzi. Pokud jsme jejı́m odstraněnı́m vytvořili ε-pravidla nebo jednoduchá pravidla, opakujeme bod 1. 3. Pravidla, jejichž pravá strana má délku 1, již vyhovujı́ požadavkům na normálnı́ formu (a také přı́padné ε-pravidlo pro startovacı́ symbol). 4. Pravidla, jejichž pravá strana má délku ≥ 2, dále zpracováváme: • pokud pravidlo začı́ná neterminálem, dosadı́me za tento neterminál všechna pravidla, která ho přepisujı́, tj. napřı́klad pravidlo A → BbAa při existenci pravidel B → α | β | γ nahradı́me pravidly A → αbAa | βbAa | γbAa, • to provádı́me rekurzı́vně tak dlouho, dokud všechna pravidla nezačı́najı́ terminálnı́m symbolem, • Všechny terminálnı́ symboly a na pravých stranách pravidel, kromě prvnı́ho terminálu v řetězci, nahradı́me přı́slušnými symboly Na (podobně jako při převodu na Chomského normálnı́ tvar). 5. Vytvořı́me nová pravidla Na → a pro všechny terminály a. Přı́klad 5.16 Následujı́cı́ gramatiku převedeme do Greibachové normálnı́ formy. G = ({S, A, B, C}, {a, b, c}, P, S) S → aBba | baA | ε A → BaAB | bb | ε B → CBab | abc C → bCb | a Sestrojı́me ekvivalentnı́ vlastnı́ gramatiku – odstranı́me ε-pravidla. Podle množiny Nε = {S, A} je zřejmé, že existujı́ pouze dvě ε-pravidla. Jedno z nich je pro startovacı́ symbol, který se však nevyskytuje na pravé straně žádného pravidla. Proto nenı́ nutné přidávat nový neterminál a měnit startovacı́ symbol gramatiky. G0 = ({S, A, B, C}, {a, b, c}, P 0 , S) S → aBba | baA | ba | ε A → BaAB | BaB | bb B → CBab | abc C → bCb | a Levá rekurze se v této gramatice nevyskytuje. Při převodu do Greibachové normálnı́ formy nejdřı́v zajistı́me, aby nejlevějšı́m symbolem v pravých stranách pravidel byl vždy terminál. To provedeme přepsánı́m toho neterminálu, který je na začátku upravovaného řetězce. Pravidlo A → BaAB nahradı́me těmito pravidly: A → CBabaAB | abcaAB Jedno z pravidel opět začı́ná neterminálem, tedy úpravu provedeme znovu: A → bCbBabaAB | aBabaAB | abcaAB 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK 80 Pravidlo A → BaB nahradı́me pravidly A → CBabaB | abcaB Po druhé úpravě: A → bCbBabaB | aBabaB | abcaB Pravidlo B → CBab nahradı́me pravidly B → bCbBab | aBab V této fázi jsme zı́skali následujı́cı́ pravidla: S → aBba | baA | ba | ε A → bCbBabaAB | aBabaAB | abcaAB | bCbBabaB | aBabaB | abcaB | bb B → bCbBab | aBab | abc C → bCb | a Zbývá splnit podmı́nku, podle které v pravých stranách pravidel jsou všechny symboly kromě nejlevějšı́ho neterminálnı́. GGN F = ({S, A, B, C, Na , Nb , Nc }, {a, b, c}, P 00 , S) S → aBNb Na | bNa A | bNa | ε A → bCNb BNa Nb Na AB | aBNa Nb Na AB | aNb Nc Na AB | bCNb BNa Nb Na B | | aBNa Nb Na B | aNb Nc Na B | bNb B → bCNb BNa Nb | aBNa Nb | aNb Nc C → bCNb | a Na → a Nb → b Nc → c Přı́klad 5.17 Následujı́cı́ gramatiku převedeme do Greibachové normálnı́ formy. G = ({S, A, B}, {a, b}, P, S) S → aA | AB | b A → SBa | a B → Bb | a V gramatice nejsou žádná ε-pravidla ani jednoduchá pravidla, ale je tu levá rekurze, kterou musı́me předem odstranit. Přı́má rekurze je v jednom z pravidel pro B, nepřı́má rekurze je v pravidlech pro neterminály S a A. Budeme postupovat podle algoritmu popsaného na straně 72. Stanovı́me pořadı́ neterminálů (A1 , A2 , A3 ) = (S, A, B). • i = 1, j = 1 : nenı́ třeba upravovat, pravidla pro S neobsahujı́ přı́mou rekurzi. • i = 2, j = 1 : dosadı́me pravé strany pravidel pro S do prvnı́ho z pravidel pro neterminál A: A → aABa | ABBa | bBa | a 5.3 NORMÁLNÍ FORMY BEZKONTEXTOVÝCH GRAMATIK 81 • i = 2, j = 2 : odstranı́me přı́mou rekurzi: A → aABaA0 | bBaA0 | aA0 | aABa | bBa | a A0 → BBaA0 | BBa • i = 3, j = 1, j = 2 : beze změny. • i = 3, j = 3 : odstranı́me přı́mou rekurzi: B → aB 0 | a B 0 → bB 0 | b Upravená gramatika bez levé rekurze (také nepřı́mé) je následujı́cı́: G0 = ({S, A, A0 , B, B 0 }, {a, b}, P 0 , S) S → aA | AB | b A → aABaA0 | bBaA0 | aA0 | aABa | bBa | a A0 → BBaA0 | BBa B → aB 0 | a B 0 → bB 0 | b Zajistı́me, aby pravé strany pravidel začı́naly vždy terminálnı́m symbolem. S → aA | aABaA0 B | bBaA0 B | aA0 B | aABaB | bBaB | aB | b A → aABaA0 | bBaA0 | aA0 | aABa | bBa | a A0 → aB 0 BaA0 | aBaA0 | aB 0 Ba | aBa B → aB 0 | a B 0 → bB 0 | b V dalšı́m kroku všechny terminály na pravých stranách pravidel, kromě nejlevějšı́ho, zaměnı́me za přı́slušné neterminálnı́ symboly. GGN F = ({S, A, A0 , B, B 0 , Na }, {a, b}, P 00 , S) S → aA | aABNa A0 B | bBNa A0 B | aA0 B | aABNa B | bBNa B | aB | b A → aABNa A0 | bBNa A0 | aA0 | aABNa | bBNa | a A0 → aB 0 BNa A0 | aBNa A0 | aB 0 BNa | aBNa B → aB 0 | a B 0 → bB 0 | b Na → a Úkol 5.7 Převed’te následujı́cı́ gramatiky do Greibachové normálnı́ formy. G1 = ({S, A, B}, {a, b}, P, S) S → aAB | AB | ε A → BbA | ab B → Ba | b G2 = ({S, A, B}, {a, b}, P, S) S → BAaB | aBA | ε A → aaA | SB | a B → AbA | b C 5.4 KRITÉRIA BEZKONTEXTOVOSTI G3 = ({S, A, B, C}, {a, b, c}, P, S) S → CAa | ε A → aCb | c B → cB | cS | b C → caC | A | b 5.4 5.4.1 82 G4 = ({E, F }, {i, +, (, )}, P, S) E →E+F |F F → (E ) | i Kritéria bezkontextovosti Pumping lemma S Pumping lemma jsme se seznámili už u regulárnı́ch jazyků. U bezkontextových jazyků narůstá složitost pravidel gramatiky a proto je složitějšı́ i tato věta. Když pomocı́ Pumping lemma dokazujeme, že jazyk L nenı́ bezkontextový, postupujeme takto: 1. vybereme vhodné slovo w ∈ L, a to tak, aby bylo „dostatečně dlouhé“ – mělo by tedy být určeno výrazem, ve kterém je index („proměnná“) potenciálně jdoucı́ do nekonečna, 2. vybereme vhodné rozdělenı́ w = x · y · z · u · v tak, aby • |y · z · u| ≤ q, kde q je některé konečné čı́slo, to znamená, že v celé prostřednı́ části slova můžeme jako indexy použı́t pouze konečná čı́sla (žádné „pı́smenné proměnné“), • |y · u| > 0 (v těchto dvou částech dohromady musı́ být alespoň jeden symbol), 3. zvolı́me čı́slo k ≥ 0 (většinou stačı́ vybrat k = 0 nebo k = 2) a vytvořı́me slovo wk = x · y k · z · uk · v 4. pokud wk ∈ / L, vracı́me se k bodu 2 a vyzkoušı́me dalšı́ rozdělenı́, pokud wk ∈ L, vracı́me se k bodu 1 a hledáme jiné vhodné slovo, 5. končı́me tehdy, když se v bodu 2 nedařı́ najı́t dalšı́ rozdělenı́. Účelem je pro vybrané slovo otestovat všechna možná rozdělenı́ a pro každé dojı́t k závěru, že wk ∈ / L, pak dokážeme, že jazyk L nenı́ bezkontextový. Přı́klad 5.18 Dokážeme, že jazyk L = an b2n an : n ≥ 0 nenı́ bezkontextový. Vybereme si slovo w = ai b2i ai a předpokládáme, že i je dostatečně vysoké (může růst do nekonečna). Dále si určı́me možná rozdělenı́ tohoto slova – w = x · y · z · u · v: 1) y · z · u obsahuje pouze symboly 0 a0 z prvnı́ části slova, P 5.4 KRITÉRIA BEZKONTEXTOVOSTI 83 2) y · z · u v sobě zahrnuje hranici mezi symboly 0 a0 z prvnı́ části slova a symboly 0 b0 z druhé části slova, 3) y · z · u obsahuje pouze symboly 0 b0 z prvnı́ části slova, 4) y · z · u v sobě zahrnuje hranici mezi symboly 0 b0 z druhé části slova a symboly 0 a0 z třetı́ části slova, 5) y · z · u obsahuje pouze symboly 0 a0 z prvnı́ části slova. Dalšı́ rozdělenı́ již nejsou možná, protože musı́ být splněny podmı́nky omezenı́ |y · z · u| shora a nenulové délky řetězce y · u (proto napřı́klad do y · z · u nemohou patřit symboly ze všech třı́ částı́ slova – tento řetězec by obsahoval celý podřetězec b2i , který ale nelze shora omezit). Prověřı́me všechny možnosti: ad. 1) x · y · z · u · v = (am1 ) · (am2 ) · (am3 ) · (am4 ) · ai−(m1 +m2 +m3 +m4 ) b2i ai kde m2 + m3 + m4 ≤ q, m2 + m4 > 0 x · y k · z · uk · v = am1 · akm2 · am3 · akm4 ai−(m1 +m2 +m3 +m4 ) b2i ai = = a(k−1)(m2 +m4 )+i b2i ai pro k = 0: w0 = ai−m2 −m4 b2i ai ∈ / L (protože m2 + m4 > 0) ad. 2) (x) · (y · z · u) · (v) = (am1 ) · (am2 bm3 ) · bm4 ai sestrojı́me x · y k · z · uk · v; protože vždy platı́ |y · u| > 0, pro každé k > 1 se zvyšuje: (a) počet 0 a0 indexovaných m2 , pak x · y k · z · uk · v = aj b2i ai , kde j 6= i, slovo wk ∈ / L, 0 0 k k i j i (b) počet b indexovaných m3 , pak x · y · z · u · v = a b a , kde j 6= 2i, slovo wk ∈ / L, j p k m i 0 0 0 0 k k m 4 1 (c) počet a i b indexovaných m2 a m3 , pak x · y · z · u · v = a (a b ) b a , kde k > 1, j, p ≥ 1, slovo wk ∈ / L. ad. 3) x · y · z · u · v = ai bm1 · (bm2 ) · (bm3 ) · (bm4 ) · b2i−(m1 +m2 +m3 +m4 ) ai kde m2 + m3 + m4 ≤ q, m2 + m4 > 0 x · y k · z · uk · v = ai bm1 bkm2 bm3 bkm4 b2i−(m1 +m2 +m3 +m4 ) ai = = ai b(k−1)(m2 +m4 )+2i ai pro k = 0: w0 = ai b2i−(m2 +m4 ) ai ∈ /L ad. 4) dokazujeme podobně jako bod 2) ad. 5) dokazujeme podobně jako bod 1) Pro všechna možná rozdělenı́ nám vyšlo, že pro alespoň jednu hodnotu k slovo wk nepatřı́ do jazyka L, proto jazyk L nenı́ bezkontextový. Přı́klad 5.19 n 2 o Dokážeme, že jazyk L = an : n ≥ 0 nenı́ bezkontextový. 2 Opět zvolı́me nějaké slovo w = ap ∈ L s „dostatečně velkým“ indexem p. Toto slovo rozdělı́me následovně: 2 ap = ax1 ax2 ax3 ax4 ax5 5.4 KRITÉRIA BEZKONTEXTOVOSTI 84 Pokud z těchto dvou reprezentacı́ téhož slova (na dvou stranách rovnice) vezmeme pouze délky, zı́skáme již „čı́selnou“ rovnici p2 = x1 + x2 + x3 + x4 + x5 . Podle podmı́nek Pumping lemma musı́ zároveň platit nerovnice • x2 + x4 > 0 podle podmı́nky |y · u| > 0 • x2 + x3 + x4 ≤ q podle podmı́nky |y · z · u| ≤ q Po „pumpovánı́“ s indexem k, což musı́ být nezáporné celé čı́slo, zı́skáme jinou rovnici (hodnotu m použijeme pouze pro vyjádřenı́ faktu, že slovo patřı́cı́ do jazyka L má délku rovnu druhé mocnině některého přirozeného čı́sla): m2 = x1 + k · x2 + x3 + k · x4 + x5 = k · (x2 + x4 ) + (x1 + x3 + x5 ) Zı́skali jsme rovnici, v jejı́ž obou částech oddělených rovnı́tkem jsou funkce. Zatı́mco levá část rovnice roste s mocninnou řadou, pravá lineárně (je to lineárnı́ funkce) a tedy mnohem pomaleji. Uvědomme si, že na obou stranách rovnice musı́ být výsledkem přirozené čı́slo (oborem hodnot obou funkcı́ jsou přirozená čı́sla), a dále že x1 , . . . , x5 jsou z našeho pohledu vlastně konstantnı́ čı́sla (navı́c přirozená), zatı́mco m a k jsou proměnné. At’stanovı́me čı́sla x2 a x4 jakkoliv, vždy se najde čı́slo k, pro které součet čı́sel x2 + x4 nenı́ roven druhé mocnině žádného čı́sla. Proto jazyk L nenı́ bezkontextový. Přı́klad 5.20 n Pomocı́ Pumping lemma dokážeme, že jazyk L = a2 : n ≥ 0 nenı́ bezkontextový. Důkaz bude podobný předchozı́mu. r Zvolı́me nějaké slovo w = a2 ∈ L s „dostatečně velkým“ indexem r. Toto slovo rozděr lı́me na a2 = ax1 ax2 ax3 ax4 ax5 a musı́ platit x2 + x4 > 0, x2 + x3 + x4 ≤ q Před pumpovánı́m: 2r = x1 + x2 + x3 + x4 + x5 Po pumpovánı́: 2m = x1 + k · x2 + x3 + k · x4 + x5 = k · (x2 + x4 ) + (x1 + x3 + x5 ) Zı́skali jsme rovnici, v jejichž obou částech oddělených rovnı́tkem jsou funkce. Zatı́mco levá část rovnice roste exponenciálně, pravá lineárně (je to lineárnı́ funkce) a tedy mnohem pomaleji. Ze stejných důvodů jako v předchozı́m přı́kladu lze vyvodit, že jazyk L nenı́ bezkontextový. Přı́klad 5.21 Dokážeme, že jazyk L = {ww : w ∈ {a, b}∗ } nenı́ bezkontextový. Tento jazyk se skládá ze slov, jejichž obě poloviny jsou stejné. 5.4 KRITÉRIA BEZKONTEXTOVOSTI 85 Pro důkaz si vybereme slovo w = ap bp ap bp , p > 4, q = 4 a dokážeme, že pro toto slovo neexistuje žádné rozdělenı́, které by umožňovalo „pumpovánı́“ dle Pumping lemma. Podmı́nky s čı́slem 4 se mohou zdát podivné, ale i tak je zřejmé, že slovo splňuje podmı́nky z Pumping lemma – index p může být dostatečně velký a prostřednı́ část je shora omezena. x y z u v xy i zui v ap bp−1 ap bp−1 ap bp−1 ... b ε ε ε b ε a a ba ap−1 bp ap−1 bp ap−1 bp ap bp−1+i ap−1+i bp ap bp ap−1+i bp ap bp−1 (ba)i ap−1 bp pro i = 0 ap bp−1 ap−1 bp ap bp ap−1 bp ap bp−1 ap−1 bp ∈ /L ∈ /L ∈ /L Jak vidı́me, většinou nám úplně stačı́ pro každé rozdělenı́ otestovat slovo pro i = 0. Můžeme pokračovat dalšı́mi řádky tabulky, ale vždy dojdeme ke stejnému závěru. Je to proto, že když je část yzu omezena konstantou, může zabrat nejvýše dvě různé části ze čtyř částı́ vybraného slova w (a přitom do yu musı́ padnout alespoň jeden symbol slova), tedy po iteraci pro žádné i kromě i = 1 nedostaneme slovo, jehož obě poloviny jsou stejné. Proto L nenı́ bezkontextový. Úkol 5.8 1. Dokažte, že jazyk L1 = {an bm an : 0 ≤ m ≤ n} nenı́ bezkontextový. Použijte podobný postup jako je v přı́kladu 5.18, a také nerovnosti v zadánı́ jazyka. C 2. Ukažte, že pro jazyk L2 = an b2n an bm : m, n ≥ 0 podmı́nka z Pumping lemma platı́, třebaže nejde o bezkontextový jazyk. Proč? 5.4.2 Parikhův vektor Parikhův vektor slova obsahuje informaci o počtu jednotlivých symbolů v daném slově, Parikhův vektor jazyka obsahuje informaci o počtu jednotlivých symbolů ve slovech daného jazyka (je to množina Parikhových vektorů všech slov jazyka). Jde tedy o redukci informace na pouhý počet, bez ohledu na konkrétnı́ umı́stěnı́ symbolů ve slovech. Přı́klad 5.22 Je dán jazyk L = ai cb2i+2 a5 | i ≥ 0 ∪ ai cb3i+2 a5 | i ≥ 0 . Vytvořı́me Parikhův vektor slova a3 cb8 a5 . Pracujeme nad abecedou Σ = {a, b, c}. Stanovı́me pořadı́ symbolů v této abecedě, můžeme použı́t pořadı́, ve kterém jsou zde prvky množiny vyjmenovány. Parikhův vektor bude třı́prvkový, jeho prvky odpovı́dajı́ počtu symbolů ve slově podle stanoveného pořadı́. Spočı́táme symboly ve slově a vytvořı́me vektor: ψ(a3 cb8 a5 ) = (3 + 5, 8, 1) = (8, 8, 1) 5.4 KRITÉRIA BEZKONTEXTOVOSTI 86 Přı́klad 5.23 Vytvořı́me Parikhův vektor jazyka z předchozı́ho přı́kladu. Definice jazyka se skládá ze dvou částı́. Z prvnı́ části zı́skáme tento vektor: ψ1 (L) = {(i + 5, 2i + 2, 1) | i ≥ 0} = {i(1, 2, 0) + (5, 2, 1) | i ≥ 0} (rozdělı́me na část závislou na i a část konstantnı́). Podle druhé části zjistı́me tento vektor: ψ2 (L) = {(i + 5, 3i + 2, 1) | i ≥ 0} = {i(1, 3, 0) + (5, 2, 1) | i ≥ 0} Nynı́ můžeme sestrojit Parikhův vektor jazyka L: ψ(L) = ψ1 (L) ∪ ψ2 (L) = {i(1, 2, 0) + (5, 2, 1) | i ≥ 0} ∪ {i(1, 3, 0) + (5, 2, 1) | i ≥ 0} Vidı́me, že se jedná o množinu Parikhových vektorů, do této množiny patřı́ také vektor (8, 8, 1) slova a3 cb8 a5 z předchozı́ho přı́kladu. Semilineárnı́ množina je sjednocenı́ konečně mnoha lineárnı́ch množin (třeba lineárnı́ch množin Parikhových vektorů). Parikhův vektor každého bezkontextového jazyka je vždy semilineárnı́ množina. V přı́kladu 5.23 jsme vytvořili Parikhův vektor bezkontextového jazyka. Jde o sjednocenı́ lineárnı́ch množin (konstantnı́ vektory jsou násobeny proměnnou i bez mocniny, tedy lineárnı́ funkce). Přı́klad 5.24 Vytvořı́me Parikhův vektor bezkontextového jazyka L = ai+3 bj a2i b | i, j ≥ 1 . ψ(L) = {i(3, 0) + j(0, 1) + (3, 1) | i, j ≥ 1}, opět jde o semilineárnı́ množinu. Jde o implikaci (jestliže je jazyk bezkontextový, pak jeho Parikhův vektor je semilineárnı́ množina), proto Parikhův vektor lze použı́t jako kritérium bezkontextovosti pouze důkazem, že Parikhův vektor daného jazyka nenı́ semilineárnı́ množina. Přı́klad 5.25 n Dokážeme, že jazyk L = an b2 +5n+2 a3 | n ≥ 0 nenı́ bezkontextový. Sestrojı́me jeho Parikhův vektor: ψ(L) = {2n (0, 1) + n(1, 5) + (3, 2) | n ≥ 0} Tato množina nenı́ semilineárnı́ (resp. lineárnı́, nic dalšı́ho nesjednocujeme), protože jeden z vektorů násobı́me exponenciálnı́ hodnotou 2n . Proto jazyk L nenı́ bezkontextový. Ke každému bezkontextovému jazyku lze sestrojit regulárnı́ jazyk se stejným Parikhovým vektorem. Postup si ukážeme na přı́kladu. 5.4 KRITÉRIA BEZKONTEXTOVOSTI 87 Přı́klad 5.26 Podle jazyka L = a2n cbn ca3m b | n ≥ 1, m ≥ 0 sestrojı́me regulárnı́ jazyk R se stejným Parikhovým vektorem. Sestrojı́me Parikhův vektor jazyka L: ψ(L) = {n(2, 1, 0) + m(3, 0, 0) + (0, 1, 2) | n ≥ 1, m ≥ 0} Regulárnı́ výraz popisujı́cı́ jazyk R pak sestavı́me podle Parikhova vektoru: R = (aab)∗ aab (aaa)∗ ccb Všimněte si rozdı́lu mezi prvnı́ a druhou částı́ regulárnı́ho výrazu – protože n ≥ 1, musı́ být obsah iterované závorky uveden ještě jednou (počet nejméně 1). Existuje vı́ce řešenı́. Napřı́klad regulárnı́ jazyk (aaa)∗ aab(aab)∗ ccb má také stejný Parikhův vektor. Úkol 5.9 C 1. Sestrojte Parikhův vektor těchto slov nad abecedou Σ = {a, b, c}: • aabacbb • bcabcabca • caacbbbc • aaab • baaccbbb • ε 2. Sestrojte Parikhův vektor těchto jazyků: • L1 = a2i bi | i ≥ 1 • L2 = a2i bi+3 | i ≥ 1 • L3 = a2i bi+j+1 cj | i, j ≥ 0 • L4 = nbn a2n bn | n ≥ 0 o n • L5 = bn a2 bk | n, k ≥ 0 n • L6 = bn a2 b | 1 ≤ n ≤ 4 3. Jazyky L4 a L5 z předchozı́ úlohy zjevně nejsou bezkontextové. U kterého z nich je to možné dokázat pomocı́ Parikhova vektoru a u kterého ne? Proč? n 4. Dokažte pomocı́ Parikhova vektoru, že jazyk L = an bn textový. 2 +1 o a2 | n ≥ 1 nenı́ bezkon- 5. Pro všechny jazyky z úlohy 2, které jsou bezkontextové, sestrojte regulárnı́ jazyky se stejným Parikhovým vektorem. Kapitola 6 Jazyky typu 0 Jazyky typu 0 v Chomského hierarchii nám popisujı́ vše, co je vypočitatelné, algoritmizovatelné. Problém je vypočitatelný (algoritmizovatelný), právě když existuje jazyk typu 0, který jej popisuje. Protože jazyky typu 0 jsou rozpoznávány Turingovými stroji, můžeme řı́ct, že problém je vypočitatelný právě tehdy a jen tehdy, pokud lze sestrojit Turingův stroj, který jej počı́tá. 6.1 6.1.1 Turingův stroj Co je to Turingův stroj – pojmy a značenı́ Z přednášek vı́me, že Turingův stroj je abstraktnı́ výpočetnı́ model. Je určen takto: M = (Q, Σ, Γ, δ, q0 , F, {−1, 0, 1}), kde • Q je neprázdná konečná množina stavů, • Σ je neprázdná konečná vstupnı́ abeceda, • Γ je neprázdná konečná pásková (pracovnı́) abeceda a platı́ Σ ⊂ Γ, • δ je přechodová funkce, která musı́ být vypočitatelná, • F je neprázdná množina koncových stavů, platı́ F ⊆ Q, • {−1, 0, 1} je určenı́ možných pohybů čtecı́ a zápisové hlavy na pásce (vlevo, stát, vpravo), také může být napřı́klad {L, S, R}. Vstupnı́ abeceda obsahuje ty symboly, ze kterých se může skládat vstupnı́ slovo, jazyk rozpoznávaný Turingovým strojem je právě nad touto abecedou. Je zřejmé, že pásková abeceda obsahuje i takové symboly, které nejsou ve vstupnı́ abecedě. Je to předevšı́m speciálnı́ páskový symbol označujı́cı́ buňku, ve které nenı́ nic zapsáno, obvykle se značı́ B (ze slova Blank – „prázdný“) nebo t. Dále tam mohou být symboly určujı́cı́ začátek a konec obsazené části pásky (pokud jsou použı́vány), obvykle symboly $ nebo #. 88 P 6.1 TURINGŮV STROJ 89 Přechodová funkce má formu δ(p, a) = (q, b, M ), kde • p, q ∈ Q je výchozı́ a cı́lový stav přechodu, • a, b ∈ Γ jsou symboly páskové abecedy, předpis určuje, že přečtený symbol a má být přepsán symbolem b, • M ∈ {−1, 0, 1} určuje pohyb hlavy – určujeme, zda se má pokračovat vlevo, zůstat na mı́stě nebo se posunout o pole doprava. Přı́klad 6.1 Napřı́klad konfigurace (abbca, q, daab) znamená, že se stroj nacházı́ ve stavu q, na pásce je slovo abbcadaab a čtecı́ a zápisová hlava ukazuje na šestý symbol slova – d. Konfigurace je (α, q, β), kde α · β je slovo na pásce, α, β ∈ Γ∗ , výpočet je ve stavu q ∈ Q a čtecı́/zápisová hlava ukazuje na prvnı́ symbol řetězce β. Také lze zapsat jako αqβ. Počátečnı́ konfigurace je (ε, q0 , w0 ), kde q0 je počátečnı́ stav a w0 ∈ Σ∗ je slovo na vstupu. Koncová konfigurace je (w1 , qf , w2 ), kde qf ∈ F, w1 , w2 ∈ Γ∗ . Slovo w1 · w2 lze brát jako výstupnı́ řetězec, pokud je nějaký výstup požadován. Turingův stroj totiž nejen určuje, zda dané slovo patřı́ do jazyka jı́m rozpoznávaného, ale může provádět také nejrůznějšı́ výpočty včetně složitých matematických operacı́ – vždyt’ pro cokoliv vypočitatelného lze vytvořit Turingův stroj. Přechod mezi konfiguracemi (přesněji – jde o relaci, vzpomeňte si, co to znamená v řeči matematiky) je definován pomocı́ přechodové funkce, podobně jako u jiných typů automatů, jen je trochu komplikovanějšı́, protože existuje vı́ce možnostı́ posunu čtecı́/zápisové hlavy. Relace přechodu mezi konfiguracemi je určena takto: (α, qi , aβ) ` (αb, qj , β) ⇔ δ(qi , a) = (qj , b, 1) (6.1) (αc, qi , aβ) ` (α, qj , cbβ) ⇔ δ(qi , a) = (qj , b, −1) (6.3) (α, qi , aβ) ` (α, qj , bβ) ⇔ δ(qi , a) = (qj , b, 0) (6.2) V prvnı́m přı́padě se čtecı́ a zápisová hlava posunuje doprava, v druhém zůstává na mı́stě (tj. v dalšı́m kroku bude čı́st totéž polı́čko jako v tomto) a v třetı́m se posunuje doleva. 6.1.2 Navrhujeme Turingův stroj pro daný jazyk Při návrhu Turingova stroje postupujeme následovně: • zjistı́me si, které části slova majı́ být „synchronizovány“ – pokud je jejich délka nebo typ symbolu v nějakém vztahu, a také jakým způsobem, • určı́me stavy a odpovı́dajı́cı́ reakce, promyslı́me si „strategii“ zpracovánı́ slova včetně přechodu do koncových stavů, • sestavı́me δ-funkci, P 6.1 TURINGŮV STROJ 90 • otestujeme pro několik typických slov rozpoznávaného jazyka, předevšı́m pro nejkratšı́ slova. Přı́klad 6.2 Sestrojı́me Turingův stroj rozpoznávajı́cı́ jazyk L = {an bn cn : n ≥ 0} M = ({q0 , qP , qA , qB , qC , qf , qaccept }, {a, b, c}, {a, ā, b, b̄, c, c̄, t, $}, δ, {qaccept }, {−1, 0, 1}) Budeme postupovat takto: 1. označı́me prvnı́ symbol a na pásce (tj. přepı́šeme symbolem ā), 2. najdeme prvnı́ b, označı́me ho, 3. pak najdeme prvnı́ c, taktéž označı́me, 4. přejdeme na začátek (postupujeme doleva, dokud nenajdeme nejbližšı́ označené ā), 5. posuneme se o polı́čko dále na prvnı́ neoznačené a, 6. návrat k bodu 1, 7. po označenı́ všech symbolů a a k nim přı́slušejı́cı́ch b a c ještě zkontrolujeme, zda neexistujı́ některé neoznačené symboly b a c a ukončı́me výpočet. Cyklus naznačený v bodech 1 až 6 bude probı́hat postupně přes všechny symboly a ze vstupu, tedy nkrát. Jednotlivé stavy znamenajı́: • qA – označili jsme a, přeskakujeme symboly a, b̄, hledáme prvnı́ neoznačené b, • qB – označili jsme b, přeskakujeme symboly b, c̄, hledáme prvnı́ neoznačené c, • qC – označili jsme c, vracı́me se na začátek k poslednı́mu označenému ā, při pohybu doleva přeskakujeme všechny symboly c̄, b, b̄, a. Definujeme δ funkci: δ(q0 , $) = (qaccept , 0) (přijali jsme prázdné slovo) δ(q0 , a) = (qA , ā, 1) δ(qB , b) = (qB , b, 1) δ(qP , a) = (qA , ā, 1) δ(qB , c̄) = (qB , c̄, 1) δ(qA , a) = (qA , a, 1) δ(qB , c) = (qC , c̄, −1) δ(qA , b̄) = (qA , b̄, 1) δ(qC , X) = (qC , X, −1), δ(qA , b) = (qB , b̄, 1) X ∈ {a, b, b̄, c̄} δ(qC , ā) = (qP , ā, 1) δ(q0 , b̄) = (qf , b̄, 1) δ(qf , b̄) = (qf , b̄, 1) δ(qf , c̄) = (qf , c̄, 1) δ(qf , $) = (qaccept , $, 0) Ukázka zpracovánı́ slova abc: (ε, q0 , abc) ` (ā, qA , bc) ` (āb̄, qB , c) ` (ā, qC , b̄c̄) ` (ε, qC , āb̄c̄) ` (ā, q0 , b̄c̄) ` ` (āb̄, qf , c̄) ` (āb̄c̄, qf , ε) ` (āb̄c̄, qaccept , ε) Jestliže zaznamenáme i hraničnı́ symboly, zpracovánı́ bude vypadat takto: ($, q0 , abc$) ` ($ā, qA , bc$) ` ($āb̄, qB , c$) ` ($ā, qC , b̄c̄$) ` ($, qC , āb̄c̄$) ` ` ($ā, q0 , b̄c̄$) ` ($āb̄, qf , c̄$) ` ($āb̄c̄, qf , $) ` ($āb̄c̄, qaccept , $) 6.1 TURINGŮV STROJ 91 Ukázka zpracovánı́ slova aabbcc: (ε, q0 , aabbcc) ` ` (āab̄, qC , bc̄c) ` (āā, qA , b̄bc̄c) ` (āāb̄, qC , b̄c̄c̄) ` (āāb̄b̄, qf , c̄c̄) (ā, qA , abbcc) ` (āa, qA , bbcc) ` ` (āa, qC , b̄bc̄c) ` (ā, qC , ab̄bc̄c) ` (āāb̄, qA , bc̄c) ` (āāb̄b̄, qB , c̄c) ` (āā, qC , b̄b̄c̄c̄) ` (ā, qC , āb̄b̄c̄c̄) ` (āāb̄b̄c̄, qf , c̄) ` (āāb̄b̄c̄c̄, qf , ε) (āab̄, qB , bcc) ` (āab̄b, qB , cc) ` ` (ε, qC , āab̄bc̄c) ` (ā, q0 , ab̄bc̄c) ` ` (āāb̄b̄c̄, qB , c) ` (āāb̄b̄, qC , c̄c̄) ` ` (āā, q0 , b̄b̄c̄c̄) ` (āāb̄, qf , b̄c̄c̄) ` ` (āāb̄b̄c̄c̄, qaccept , ε) Ukázka zpracovánı́ slova ac: (ε, q0 , ac) ` (ā, qA , c) chyba ⇒ slovo ac nenı́ přijato. Ukázka zpracovánı́ slova ε: (ε, q0 , ε) ` (ε, qaccept , ε) Přı́klad 6.3 Vytvořı́me Turingův stroj rozpoznávajı́cı́ jazyk L = {wcw : w ∈ {a, b}∗ } Opět si nejdřı́v promyslı́me, jak by měl stroj postupovat. Předpokládejme, že na vstupu je slovo abbcabb. Nejdřı́v označı́me symbol v prvnı́ polovině slova, pak se přesuneme do druhé části slova a najdeme párový symbol. Potom je třeba se přesunout zpět do prvnı́ části slova, označit dalšı́ symbol, atd. Může to vypadat napřı́klad takto: začátek zpracovánı́: abb c abb označı́me prvnı́ symbol: xbb c abb označı́me k němu párový symbol: xbb c xbb podobně po druhém kroku: xxb c xxb po třetı́m kroku: xxx c xxx V předchozı́m přı́kladu jsme počı́tali s tı́m, že slovo na vstupu je ohraničeno symboly $. Nynı́ si ukážeme postup, ve kterém slovo nenı́ těmito symboly ohraničeno, ale za poslednı́m symbolem slova jsou symboly t. Když se nad tı́m zamyslı́me, obě řešenı́ jsou ekvivalentnı́, protože t můžeme také chápat jako hraničnı́ symboly a stejně s nimi zacházet. Použijeme tyto stavy: • q0 – označı́me symbol v prvnı́ části slova, na který ukazuje čtecı́/zápisová hlava, přejdeme do stavu qA nebo qB podle toho, zda jsme označili symbol a nebo b, posun vpravo, • qA – byl označen symbol a, posunujeme se v cyklu doprava a hledáme polovinu slova (symbol c, tedy přeskakujeme všechny symboly a a b, které jsou před c), • qB – totéž pro b, • qAC – pokračovatel stavu qA , přecházı́me do něj, pokud ve stavu qA najdeme symbol c, hledáme párový symbol (symbol a v druhé části slova, tedy přeskakujeme vše, co bylo označeno – symboly x), 6.1 TURINGŮV STROJ 92 • qBC – podobně ze stavu qB , • qZC – v druhé části slova jsme ve stavech qAC nebo qBC našli párový symbol, označili, ted’ se v cyklu posunujeme zpět doleva a hledáme polovinu slova (symbol c, přeskakujeme vše označené – symboly x), • qZ – ve stavu qZC jsme našli polovinu slova, nynı́ budeme pokračovat doleva a hledat poslednı́ označený symbol, přeskakujeme symboly a a b, až najdeme nejpravějšı́ x, změnı́me stav na q0 , posuneme se o krok doprava (tj. čtecı́/zápisová hlava bude ukazovat na prvnı́ neoznačený symbol nebo v přı́padě všech označených na c) a v cyklu se vracı́me na začátek tohoto postupu, • qf – do tohoto stavu přecházı́me, pokud jsme ve stavu q0 mı́sto neoznačeného symbolu a nebo b našli přı́mo symbol c, tj. prvnı́ část slova je celá označená; nynı́ musı́me v cyklu směrem doprava projı́t zbytek slova, zda tam nezůstaly neoznačené symboly, • qaccept – do tohoto stavu přejdeme, pokud ve stavu qf najdeme konec slova (symbol t), což znamená, že je označena i celá druhá část. Turingův stroj bude definován následovně: M = ( {q0 , qA , qAC , qB , qBC , qZ , qZC , qf , qaccept }, {a, b, c}, {a, b, c, x, t}, δ, {qaccept }, {−1, 0, 1}) Nalezeno a: δ(q0 , a) = (qA , x, 1) δ(qA , Y ) = (qA , Y, 1), Y ∈ {a, b} δ(qA , c) = (qAC , c, 1) δ(qAC , x) = (qAC , x, 1) δ(qAC , a) = (qZC , x, −1) Nalezeno b: δ(q0 , b) = (qB , x, 1) δ(qB , Y ) = (qB , Y, 1), Y ∈ {a, b} δ(qB , c) = (qBC , c, 1) δ(qBC , x) = (qBC , x, 1) δ(qBC , b) = (qZC , x, −1) Návrat doleva: δ(qZC , x) = (qZC , x, −1) δ(qZC , c) = (qZ , c, −1) δ(qZ , Y ) = (qZ , Y, −1), Y ∈ {a, b} δ(qZ , x) = (q0 , x, 1) Ukončenı́ s kontrolou: δ(q0 , c) = (qf , c, 1) δ(qf , x) = (qf , x, 1) δ(qf , t) = (qaccept , t, 0) Ukázka zpracovánı́ slova aca: (ε, q0 , aca) ` (x, qA , ca) ` (xc, qAC , a) ` (x, qZC , cx) ` (ε, qZ , xcx) ` (x, q0 , cx) ` ` (xc, qf , x) ` (xcx, qf , t) ` (xcx, qaccept , t) Ukázka zpracovánı́ slova c (to je nejkratšı́ slovo jazyka): (ε, q0 , c) ` (c, qf , t) ` (c, qaccept , t) 6.1 TURINGŮV STROJ 93 Přı́klad 6.4 Sestrojı́me Turingův stroj, který pro zadaný vstup vytvořı́ jeho reverzi (tj. pro slovo w na vstupu bude na výstupu wR ). Na vstupu mohou být jakákoliv slova nad abecedou {a, b}. Předpokládejme, že na vstupu je slovo aabab. Nejdřı́v na konec slova přidáme symbol #, kterým označı́me rozhranı́ mezi původnı́m a novým slovem, a pak začneme kopı́rovat původnı́ slovo na pásku za #. Původnı́ načı́táme zprava (od konce, přitom přepisujeme symboly, abychom poznali, co už je zkopı́rováno), nové slovo naopak tvořı́me zleva, aby bylo revertováno (převráceno). Může to vypadat napřı́klad takto: začátek zpracovánı́: označı́me hranici: označı́me prvnı́ symbol: zapı́šeme k němu párový symbol: podobně po druhém kroku: po třetı́m kroku: po čtvrtém kroku: po pátém kroku: po ukončenı́: Stavy: aabab aabab # aabax # aabax # b aabxx # ba aaxxx # bab axxxx # baba xxxxx # babaa babaa • q0 – projdeme celé slovo, na konec přidáme #, pak „couvnutı́“ před # a přechod do stavu qZ , • qZ – v cyklu postupujeme doleva a přeskakujeme označené symboly, zastavı́me se na nejbližšı́m neoznačeném symbolu, pak přechod do qA nebo qB , • qA – právě jsme označili a, v cyklu postupujeme doprava a přeskakujeme označené symboly, zastavı́me se až na symbolu #, přechod do qAH , • qB – podobně jako qA , ale pro symbol b, • qAH – v cyklu postupujeme doprava a přeskakujeme všechny zapsané symboly z pravé části slova, hledáme konec, pak zapı́šeme a a přecházı́me do stavu qZH , • qBH – podobně jako qAH , ale pro symbol b, • qZH – v cyklu postupujeme doleva až k symbolu #, přeskakujeme všechny zapsané symboly a a b, pak přejdeme do stavu qZ , • qf – úklid po kopı́rovánı́, smažeme původnı́ slovo a dodaný hraničnı́ symbol #, • qaccept – koncový stav. M = ( {q0 , qA , qAH , qB , qBH , qZ , qZH , qf , qaccept }, {a, b, c}, {a, b, x, t, #}, δ, {qaccept }, {−1, 0, 1}) 6.1 TURINGŮV STROJ 94 Začátek – přı́prava: δ(q0 , Y ) = (q0 , Y, 1), Y ∈ {a, b} δ(q0 , t) = (qZ , #, −1) Ukončenı́ s úklidem: δ(qZ , t) = (qf , t, 1) δ(qf , x) = (qf , t, 1) δ(qf , #) = (qaccept , t, 0) Posun zpět – doleva: δ(qZ , x) = (qZ , x, −1) δ(qZ , a) = (qA , x, 1) δ(qZ , b) = (qB , x, 1) δ(qZH , Y ) = (qZH , Y, −1) δ(qZH , #) = (qZ , #, −1) Našli jsme a: δ(qA , x) = (qA , x, 1) δ(qA , #) = (qAH , #, 1) δ(qAH , Y ) = (qAH , Y, 1) δ(qAH , t) = (qZ H, a, −1) Našli jsme b: δ(qB , x) = (qB , x, 1) δ(qB , #) = (qBH , #, 1) δ(qBH , Y ) = (qBH , Y, 1) δ(qBH , t) = (qZ H, b, −1) Všimněme si rozdı́lu mezi činnostı́ Turingova stroje a dřı́ve definovaných automatů: • Turingův stroj nejen čte vstupnı́ pásku, může ji i zapisovat, • čtecı́ a zápisová hlava se může (nemusı́) pohybovat různými směry, nejen doprava, L • nepotřebujeme zásobnı́k, ale přesto můžeme uchovávat i jinou informaci než označenı́ stavu, ve kterém právě jsme – kamkoliv na pásku si můžeme cokoliv poznamenat a později tuto informaci využı́t, • na Turingově stroji lze provádět i výpočty. Turingovými stroji se budeme podrobněji zabývat v navazujı́cı́m kurzu Teorie vyčı́slitelnosti a složitosti. Úkol 6.1 1. Sestrojte Turingovy stroje rozpoznávajı́cı́ jazyky • L1 = {an b2n an : n ≥ 1} • L2 = {wcwcw : w ∈ {a, b}∗ } • L3 = {wcwR cw : w ∈ {a, b}∗ } 2. Podle Turingova stroje z přı́kladu 6.4 zpracujte slova bba a ε. Dále upravte definici tohoto stroje tak, aby zpracovával slova nad abecedou {a, b, c} (tj. přidejte do předpisu δ-funkce zpracovánı́ symbolů c). 3. Navrhněte (alespoň rámcově, popisem činnosti) Turingův stroj, který zdvojı́ svůj vstup (tj. napřı́klad pokud je na vstupu slovo abb, na výstupu bude slovo abbabb). 4. Navrhněte Turingův stroj, který bude mı́t na vstupu čı́slo v desı́tkové soustavě a jeho výstupem bude toto čı́slo vynásobené dvěma. C 6.2 GRAMATIKY TYPU 0 6.2 6.2.1 95 Gramatiky typu 0 Návrh gramatiky typu 0 Gramatika typu 0 je taková gramatika, jejı́ž pravidla jsou ve tvaru α → β, α ∈ (N ∪ T )∗ N (N ∪ T )∗ , β ∈ (N ∪ T )∗ Jde tedy o obecné gramatiky, kde v předpisu máme jediný požadavek – na levé straně pravidla musı́ být řetězec terminálnı́ch a neterminálnı́ch symbolů obsahujı́cı́ alespoň jeden neterminál. Přı́klad 6.5 n Následujı́cı́ gramatika generuje jazyk L = a2 : n ≥ 1 . G = ({S, A, B, C, D, E}, {a}, P, S) S → ACaB Ca → aaC CB → DB CB → E aD → Da AD → AC aE → Ea AE → ε Odvodı́me nejkratšı́ slovo generované gramatikou: S ⇒ ACaB ⇒ AaaCB ⇒ AaaE ⇒ AaEa ⇒ AEaa ⇒ aa Všimněte si „stěhovánı́“ symbolu E doleva pomocı́ pravidla AE → EA. Takový postup nenı́ v bezkontextových gramatikách možný. Přı́klad 6.6 Sestrojı́me gramatiku generujı́cı́ jazyk L = {an bn cn : n ≥ 1}. Na začátku vytvořı́me neterminálovou „kostru“. Použijeme neterminály H1 a H2 jako hranice částı́ slova obsahujı́cı́ch různé symboly, dále neterminály A a B jako „běžce“ určujı́cı́ právě zpracovávanou část slova (přidáváme bud’ zároveň symboly a a b, a nebo symbol c), dále neterminál Z pro návrat, kdy se připravujeme na generovánı́ dalšı́ trojice symbolů a, b, c, a konečně neterminál E zajišt’ujı́cı́ správné ukončenı́ generovánı́ slova a odstraněnı́ hraničnı́ch symbolů. Postup si můžeme nastı́nit na sekvenci větných forem, které by podle pravidel gramatiky byly generovány – viz tabulku 6.1. P 6.2 GRAMATIKY TYPU 0 začátek generovánı́ dalšı́ trojice a, b, c vygenerujeme a, b, posun zpracovánı́ doprava posun zpracovánı́ doprava postupně „přeskočı́me“ všechna b . . . vygenerujeme c za symbolem H2 , posun doleva posun doleva, přeskočı́me b pořád doleva . . . navážeme na začátek 96 aaaA H1 bbb H2 ccc aaaa H1 bBbbb H2 ccc aaaa H1 bbBbb H2 ccc aaaa H1 bbbbB H2 ccc aaaa H1 bbbbZ H2 cccc aaaa H1 bbbZb H2 cccc aaaa H1 Zbbbb H2 cccc aaaaA H1 bbbb H2 cccc Tabulka 6.1: Postup generovánı́ slova v gramatice typu 0 Budeme postupovat takto: • Nejdřı́v vygenerujeme hraničnı́ symboly a neterminál A. S → AH1 H2 • Vygenerujeme a a b, přesun doprava. AH1 → aH1 bB • Posouvánı́ symbolu B doprava: Bb → bB • Konec rekurze z předchozı́ho bodu, vygenerujeme c, posun zpět doleva. BH2 → ZH2 c • Posun symbolu Z doleva: bZ → Zb • Konec rekurze z předchozı́ho bodu, navážeme na prvnı́ bod. H1 Z → AH1 • Zajistı́me správné ukončenı́ generovánı́ terminálnı́ho slova. AH1 → abE Eb → bE EH2 → c Celá gramatika: G = ({S, A, B, H1 , H2 , Z, E}, {a, b, c}, P, S) S → AH1 H2 | abE AH1 → aH1 bB Bb → bB BH2 → ZH2 c bZ → Zb H1 Z → AH1 Eb → bE EH2 → c 6.2 GRAMATIKY TYPU 0 97 Vygenerujeme slovo a2 b2 c2 , modře je vyznačena část slova přepisovaná v dalšı́m kroku některým pravidlem: S ⇒ AH1 H2 ⇒ aH1 bBH2 ⇒ aH1 bZH2 c ⇒ aH1 ZbH2 c ⇒ aAH1 bH2 c ⇒ aabEbH2 c ⇒ ⇒ aabbEH2 c ⇒ aabbcc Úkol 6.2 2 1. V gramatice z přı́kladu 6.5 vygenerujte slovo a2 = a4 . C 2. Ke gramatice z přı́kladu 6.5 přidejte ještě jedno pravidlo tak, aby generovala jazyk n L = a2 : n≥0 . 3. V gramatice z přı́kladu 6.6 vygenerujte slovo a3 b3 c3 . 4. Gramatiku z přı́kladu 6.6 upravte tak, aby generovala jazyk • L1 = {an bn cn : n ≥ 0} • L2 = a2n bn c2n : n ≥ 1 6.2.2 Kurodova normálnı́ forma Obecný tvar pravidla může být trochu problém předevšı́m v důkazech. Proto v přı́padě, že gramatiku 0 chceme využı́t pro některý účel, ji převedeme do normálnı́ formy. Pro gramatiky typu 0 existuje Kurodova normálnı́ forma (anglicky Kuroda Normal Form). Gramatika typu 0 je v Kurodově normálnı́ formě (KNF) pro jazyky typu 0, jestliže pro všechna jejı́ pravidla α → β platı́ |α| ≤ 2, |β| ≤ 2 (a samozřejmě musı́ splňovat předpis pro pravidla gramatiky typu 0, tedy alespoň jeden neterminál na levé straně pravidla). Obvykle se předpokládá, že všechna pravidla gramatiky G = (N, T, P, S) v KNF splňujı́ jeden z těchto tvarů: • AB → CD, A, B, C, D ∈ N • A → CD, A, C, D ∈ N • A → a, A ∈ N, a ∈ T • A → ε, A ∈ N Všimněte si, že se připouštı́ i jeden typ zkracujı́cı́ho pravidla (poslednı́ typ), protože gramatiky typu 0 obecně nejsou nezkracujı́cı́ (to jsou až gramatiky typu 1). Vidı́me, že předpis druhého a třetı́ho typu pravidla je podobný předpisu pro Chomského normálnı́ formu. Pokud se jedná o pravidlo bezkontextového typu (i taková mohou být v gramatice typu 0), pak pro takové pravidlo můžeme použı́t algoritmus pro převod do Chomského normálnı́ formy. 6.2 GRAMATIKY TYPU 0 98 Postup převodu gramatiky typu 0 do KNF: 1. Odstranı́me jednoduchá pravidla. 2. Všechny terminály a ∈ T (na levé i pravé straně pravidla) nahradı́me „pomocnými“ neterminály Na . 3. Pro všechny neterminály vytvořené v předchozı́m bodu přidáme pravidlo Na → a. 4. Pravidla A → B1 B2 . . . Bn , n > 2 nahradı́me pravidly A → B1 X1 X1 → B2 X2 .. . Xn−2 → Bn−1 Bn Tedy pravidla bezkontextového typu zpracováváme stejně jako pro Chomského NF. 5. Pravidla A1 A2 . . . Am → B1 B2 . . . Bm , m > 2 (obě strany pravidla stejně dlouhé) nahradı́me pravidly A1 A2 → B1 X1 X1 A3 → B2 X2 .. . Xm−2 Am → Bm−1 Bm 6. Nezkracujı́cı́ pravidla A1 A2 . . . Am → B1 B2 . . . Bn , 2 < m ≤ n nahradı́me pravidly A1 A2 → B1 X1 X1 A3 → B2 X2 .. . Xm−2 Am → Bm−1 Xm−1 Xm−1 → Bm Xm Xm → Bm+1 Xm+1 .. . Xn−2 → Bn−1 Bn 7. Zkracujı́cı́ pravidla A1 A2 . . . Am → B1 B2 . . . Bn , n < m nahradı́me pravidly A1 A2 → B1 X1 X1 A3 → B2 X2 .. . Xn−1 An+1 → Bn Xn Přı́klad 6.7 Postup si ukážeme na gramatice S → AaBC C → cBAa | bS AaBc → d BA → abcd 1 2 3 , 4 5 Xn An+2 → Xn+1 .. . Xm−3 Am−1 → Xm−2 Xm−2 Am → ε 6.2 GRAMATIKY TYPU 0 99 4 a 5 provedeme náhradu terminálů a ∈ T novými neterminály Na . V pravidlech 3 Pravidlo , nynı́ C → Nb S, nemusı́me dále zpracovávat, už odpovı́dá KNF. 1 a . 2 Nejdřı́v zpracujeme pravidla bezkontextového typu S → AX1 X1 → Na X2 X2 → BC C → Nc X3 X3 → BX4 X4 → ANa 3 a 4 – jedno je zkracujı́cı́ a druhé nezkracujı́cı́: Zbývajı́ pravidla BA → Na X5 Na X5 → Nb X6 X6 → Nc Nd ANa → Nd X7 X7 B → X8 X8 Nc → ε Celá gramatika po převodu do KNF: S → AX1 X1 → Na X2 X2 → BC C → Nc X3 Úkol X3 → BX4 X4 → ANa BA → Na X5 Na X5 → Nb X6 X6 → Nc Nd ANa → Nd X7 X7 B → X8 X8 Nc → ε 6.3 1. Převed’te do KNF gramatiky z přı́kladů 6.5 a 6.6 (str. 95). 2. Převed’te do tvaru KNF následujı́cı́ gramatiku: G = ({S, A, B, C}, {a, b, c, d}, P, S) S → aAB | BA | C aAa → bCba | B AC → d B → aB | CC | b C → cS | d Na → a Nb → b Nc → c Nd → d C
Podobné dokumenty
Teorie jazyku˚ a automatu˚ I - RNDr. Šárka Vavrečková, Ph.D.
RNDr. Šárka Vavrečková, Ph.D.
Dostupné na: http://vavreckova.zam.slu.cz/formal1.html
Ústav informatiky
Filozoficko-přı́rodovědecká fakulta v Opavě
Slezská univerzita v Opavě
Bezručovo ...
Bakalarske statnice
Předmět lze splnit jeho úspěšným absolvovánı́m nebo uznánı́m z předchozı́ho
studia.
Ústnı́ část státnı́ závěrečné zkoušky se skládá ze dvou předmětů, jimiž
jsou Základy mat...
Září - Časopis Život v Kristu
modlí. Jeho modlitby však nejsou „točením mlýnku“, nýbrž zápasem. Čteme zde,
že vynakládá mnoho námahy. Modlitba je
tedy zápas, námaha. A kde je zápas, tam
je i práce. Zdá se mi obtížné podrobně
po...
Detekce tautologie Bc. Jan Schindler
konstrukčnı́ prvky, které majı́ jen dva možné stavy (0, 1). Těm bude odpovı́dat taková
Booleova algebra, která má právě jen tyto dva prvky. Velmi rozsáhlé integrované obvody
- VLSI1 hr...
Minimalizace ne´uplne urcen´ych logick´ych funkcı pomocı
Minimalizace logických funkcı́ je obor, který se rozvinul s prvnı́m praktickým uplatněnı́m
Booleovy algebry v praxi. Z algebraicky zadaných logických funkcı́ se přı́mo vycházı́ nejen při
n...