diff --git a/03-recursive/recursive.tex b/03-recursive/recursive.tex index fc94970d9d00035083413e57c782fbef3a2e8f26..9337470884262a80ed4b2cb77ff741772030aee7 100644 --- a/03-recursive/recursive.tex +++ b/03-recursive/recursive.tex @@ -906,87 +906,88 @@ Nyní dokážeme, že to vyjde nastejno -- každý jazyk přijímaný obousměrn \proof Připomeňme definici TFA: je to Turingův stroj, který dostane vstup $\|<|\alpha\|>|$ a nemá ho povoleno měnit. Navíc na levé zarážce~$\|<|$ nesmí vykonat pohyb doleva -a na pravé zarážce~$\|>|$ nesmí jít doprava. Aby byl TFA podobnější konečným automatům, -výpočet začíná s~hlavou na prvním znaku slova~$\alpha$. To je ale detail: TFA můžeme snadno upravit, -aby výpočet začínal na~$\|<|$ -- stačí přidat nový počáteční stav, v~němž provedeme jeden pohyb -hlavou doprava a přejdeme do původního počátečního stavu. +a na pravé zarážce~$\|>|$ nesmí jít doprava. Každý regulární jazyk je určitě přijímán nějakým TFA, který vznikne přímočarým překladem příslušného DFA. Opačná implikace je méně triviální. Uvažme TFA rozpoznávající nějaký jazyk~$L$ a jeho výpočet nad nějakým vstupem~$\alpha$ délky~$n$. Do vstupu ještě doplníme zarážky $\alpha[-1] = \|<|$ a $\alpha[n] = \|>|$. -Potřebujeme se nějak vyrovnat s~tím, že automat se může během výpočtu na jedno -políčko vracet opakovaně. Představme si, že se díváme dovnitř výpočtu, hlava zrovna stojí -na $i$-tém políčku slova~$\alpha$, stroj se právě přepíná ze stavu~$s$ do~$s'$ a chystá se -odejít doprava do nějakého suffixu $\alpha[i+1:{}]$. -Jak bude výpočet pokračovat? Jedna možnost je, že se stroj po čase vrátí zprava na $i$-té políčko v~nějakém novém stavu, -přičemž nic kromě stavu stroje se nezměnilo (na pásku nemůžeme zapisovat). -Nebo se předtím stroj stihl zastavit a přijmout/odmítnout. -Případně se stroj zacyklil v~nekonečné smyčce, což rovněž odpovídá odmítnutí vstupu. - -Z~této úvahy plyne, že kdybychom znali chování stroje na suffixu $\alpha[i+1:{}]$ -(jakému vstupnímu stavu odpovídá jaký výstupní), mohli bychom pomocí něj určit, -co bude stroj dělat na políčku~$\alpha[i]$, aniž bychom se dívali na následující -znaky. Z~toho bychom mohli určit chování na suffixu $\alpha[i:{}]$ a tak dále. -Zkusme to provést. - -Chování stroje na suffixu vstupu $\alpha[i:{}]$ popíšeme nějakou funkcí~$f_i(s)$. -Ta dostane počáteční stav~$s\in Q\setminus\{q_+, q_-\}$, v~němž stroj vstoupí -na $\alpha[i]$. Výsledkem funkce je koncový stav~$s'\in Q$, v~němž stroj odchází -ze suffixu doleva. Pokud je $s'=q_+$, stroj se místo odejití doleva zastavil a přijal. -Pokud $s'=q_-$, stroj odmítl zastavením nebo zacyklením se. - -Ukážeme, jak sestrojit funkci~$f_i$, pokud už známe~$f_{i+1}$. -Chceme-li stanovit $f(s)$, budeme konstruovat posloupnost stavů $s_0$, $s_1$, $s_2$, \dots{} +Představme si výpočet obousměrného automatu, který právě navštívil znak~$\alpha[i]$. +Pokud se automat pohne doprava, můžeme tento krok provést i~konečným automatem. +Pokud vyčkává na místě, jenom mění stavy, takže DFA může celou posloupnost změn provést +najednou. + +Problém nastane, pokud se TFA pohybem doleva vrátí do prefixu $\alpha[{}:i]$, +který už jednou zpracoval. Uvnitř prefixu se může libovolně dlouho toulat, než nastane +jedna ze tří možností: buď prefix opustí doprava v~nějakém novém stavu a~je opět +na znaku~$\alpha[i]$. Anebo se zastaví ve stavu $q_+$ či~$q_-$, případně se zacyklí +(tím také odmítne vstup, takže je to ekvivalentní se zastavením v~$q_-$). + +Jelikož TFA nemůže měnit obsah pásky, můžeme jeho chování v~prefixu $\alpha[{}:i]$ +jednoznačně popsat nějakou funkcí $f_i: Q\setminus\{q_+, q_-\} \rightarrow Q$. +Ta jako argument dostane stav~$s$, ve kterém stroj začíná na posledním znaku prefixu, tedy +$\alpha[i-1]$. Výsledkem funkce je stav~$s'$, v~němž stroj prefix opustí pohybem vpravo. +Pokud je $s'=q_+$, stroj z~prefixu neodešel, nýbrž se zastavil a přijal. +Je-li $s'=q_-$, stroj odmítl zastavením nebo zacyklením se. + +Budeme se snažit sestrojit DFA, který si bude ve stavu udržovat chování prefixu~$f_i$ +nalevo od aktuální pozice. + +Začněme tou nejzajímavější částí: konstrukcí~$f_{i+1}$ z~$f_i$. +Nechť chceme spočítat $f_{i+1}(s)$. TFA tedy stojí na políčku $\alpha[i]$ ve stavu~$s$. +Jelikož se na toto políčko může vracet, budeme konstruovat posloupnost stavů $s_0$, $s_1$, $s_2$, \dots{} při jednotlivých návštěvách políčka $\alpha[i]$. Na počátku položíme $s_0=s$. Nyní budeme z~libovolného~$s_j$ počítat $s_{j+1}$. Podíváme se, co stroj provede, přečte-li ve stavu~$s_j$ znak~$\alpha[i]$. -Vyhodnotíme tedy přechodovou funkci $\delta(s_j, \alpha[i])$, čímž získáme instrukci, která +Vyhodnocením přechodové funkce $\delta(s_j, \alpha[i])$ získáme instrukci, která přechází do nějakého stavu~$s'_j$, zapisuje nezměněný znak~$\alpha[i]$ a možná pohybuje hlavou: \list{o} -\:Pokud hlava odchází doleva, položíme $f_i(s) = s'_j$ a jsme hotovi. +\:Pokud hlava odchází doprava, položíme $f_{i+1}(s) = s'_j$ a jsme hotovi. \:Pokud hlava zůstává na místě, položíme $s_{j+1} = s'_j$ a pokračujeme ve vytváření posloupnosti -s~několika výjimkami: Je-li $s'_j$ rovno $q_+$ nebo~$q_-$, položíme $f_i(s) = s'_j$ a skončíme. +s~několika výjimkami: Je-li $s'_j$ rovno $q_+$ nebo~$q_-$, položíme $f_{i+1}(s) = s'_j$ a skončíme. Je-li $s'_j$ rovno některému z~předešlých~$s_k$ pro $k\le j$, stroj se zacyklil, takže položíme -$f_i(s) = q_-$ a skončíme. -\:Pokud hlava odchází doprava, využijeme známou funkci $f_{i+1}$ popisující chování na suffixu -$\alpha[i+1:{}]$, takže položíme $s_{j+1} = f_{i+1}(s'_j)$. Opět ošetříme případy, kdy je tento +$f_{i+1}(s) = q_-$ a skončíme. +\:Pokud hlava odchází doleva, využijeme známou funkci $f_i$ popisující chování na prefixu +$\alpha[{}:i-1]$. Stačí položit $s_{j+1} = f_i(s'_j)$. Opět ošetříme případy, kdy je tento stav roven $q_+$, $q_-$, případně některému z~předchozích stavů v~posloupnosti. \endlist -Všimněte si, že funkce~$f_i$ je jednoznačně určena funkcí~$f_{i+1}$ a znakem $\alpha[i]$. -To nám dává návod na sestrojení konečného automatu, který bude procházet vstupem zprava doleva -a postupně přepočítávat funkci~$f_i$. Stavy automatu odpovídají všem funkcím z~$Q\setminus\{q_+,q_-\}$ do~$Q$, -takže jich je konečně mnoho. +Všimněme si, že funkce~$f_{i+1}$ je jednoznačně určena funkcí~$f_i$ a znakem $\alpha[i]$. +Můžeme tedy sestrojit konečný automat, který bude procházet vstupem zprava doleva +a postupně přepočítávat funkci~$f_i$. Stavy automatu odpovídají všem funkcím z~$Q\setminus\{q_+,q_-\}$ do~$Q$ +a~těch je konečně mnoho. Přechody jsou dány konstrukcí~$f_{i+1}$ z~$f_i$. +Po zpracování znaku $\alpha[i]$ se tedy automat nachází ve stavu~$f_{i+1}$. -Zbývá dořešit, jak automat začne a jak skončí. +Ještě je potřeba domyslet, jak automat začne. +Zkusme použít naši konstrukci pro funkci~$f_0$ -- chování TFA na prázdném prefixu. +Hlava TFA tedy stojí na levé zarážce~$\|<|$, odkud se nesmí pohnout doleva. +Konstrukce se proto nikdy nezeptá na neexistující~$f_{-1}$. +Funkce~$f_0$ je tudíž nezávislá na vstupu a může posloužit jako počáteční stav automatu. -Použijeme-li naši konstrukci pro funkci~$f_n$, tedy chování výpočtu na pravé zarážce~\|>|, -nikdy se nezeptá na neexistující~$f_{n+1}$. To proto, že stroj se na pravé zarážce nemůže -pohybovat doprava. Funkce~$f_n$ je tedy nezávislá na vstupu, takže může posloužit jako -počáteční stav automatu. +Máme tedy automat, který po přečtení vstupu $\alpha\|>|$ zná funkci~$f_{n+1}$ -- +chování na celém vstupu, stojíme-li na pravé zarážce~$\|>|$. -Až automat přečte levou zarážku~$\|<|$, bude jeho stav roven funkci~$f_{-1}$. -Ta popisuje chování na celém vstupu. Takže do ní chceme dosadit počáteční stav~$q_0$ -původního TFA. -Je-li $f_{-1}(q_0)$ rovno~$q_+$, vstup přijmeme. -Pokud je rovno~$q_-$, tak odmítneme. -Nic jiného nemůže funkce vrátit, protože by to znamenalo, že automat z~\|<| odešel doleva, a~to nesmí. -Přijímací stavy tedy odpovídají těm funkcím~$f$, pro něž je $f(q_0)=q_+$. +Teď se nabízí do této funkce dosadit počáteční stav TFA~$q_0$, a~tím zjistit, +jaký výsledek TFA vydá. To ale nemůžeme provést: hlava TFA začíná na levém +okraji vstupu, nikoliv na~$\|>|$. Pomoc je naštěstí snadná: TFA upravíme, aby +začínal na~$\|>|$ v~nějakém novém stavu~$q'_0$. V~něm půjde doleva, dokud nenarazí +na~$\|<|$, načež udělá krok doprava a přepne se do původního počátečního stavu~$q_0$. -Tím jsme sestrojili automat dosvědčující regularitu. Ovšem ne původního jazyka~$L$, -nýbrž jazyka $L' = L\rev\cdot\{\|<|\}$, v~němž jsou slova pozpátku a ukončená zarážkou. +Je-li tedy $f_{n+1}(q'_0)$ rovno~$q_+$, TFA vstup přijal, takže DFA přijme také. +Podobně je-li rovno~$q_-$, tak odmítne. +Nic jiného nemůže funkce vrátit, protože by to znamenalo, že automat z~\|>| odešel doprava, a~to nesmí. +Přijímací stavy tedy odpovídají těm funkcím~$f$, pro něž je $f(q'_0)=q_+$. -Teď si stačí uvědomit, že z~regularity~$L'$ plyne regularita~$L\rev$ a z~ní zase regularita~$L$. -Regularitu $L\rev$ můžeme zdůvodnit cvičením \exref{quotex}, protože $L\rev$ je kvocient $L' / \{\|<|\}$. +Tím jsme sestrojili automat dosvědčující regularitu. Ovšem ne původního jazyka~$L$, +nýbrž jazyka $L' = L\cdot\{\|>|\}$, v~němž jsou slova ukončená zarážkou. +Uvědomíme si, že z~regularity~$L'$ plyne regularita~$L$. +To můžeme zdůvodnit cvičením \exref{quotex}, protože $L$ je kvocient $L' / \{\|>|\}$. Nebo to lze provést přímo úpravou koncových stavů: za koncové prohlásíme ty stavy, z~nichž vede přechod -před znak~$\|<|$ do některého z~původních koncových stavů. -Regularitu~$L$ dostaneme z~uzavřenosti regulárních jazyků na otočení (cvičení \exref{regrev}). - +před znak~$\|>|$ do některého z~původních koncových stavů. \qed \exercises