Turingův stroj (TS) je teoretický model počítače popsaný matematikem Alanem Turingem, který se používá pro modelování algoritmů v teorii vyčíslitelnosti.
Skládá se z řídicí jednotky s konečným počtem stavů, konečné množiny pravidel, která definují přechodovou funkci, a pravostranně nekonečné pásky pro vstup a zápis mezivýsledků.
Jeden ze způsobů vyjádření Churchovy–Turingovy teze říká, že ke každému algoritmu existuje ekvivalentní Turingův stroj.
Od výpočetní síly Turingova stroje se odvozuje turingovská úplnost: turingovsky úplné jsou právě ty programovací jazyky nebo počítače, které mají stejnou výpočetní sílu jako Turingův stroj.
Turingův stroj je uspořádaná sedmice M = ( Q , Γ , b , Σ , q 0 , δ , F ) {\displaystyle \ M=(Q,\Gamma ,b,\Sigma ,q_{0},\delta ,F)} , kde:
Konfigurace Turingova stroje je tvořena aktuálním stavem q, počátečním úsekem pásky s obsahujícím neprázdné symboly, a informací o pozici hlavy (tvořenou např. číslem buňky n).
Formálně může být konfigurace definována jako uspořádaná trojice ( q , s , n ) , {\displaystyle (q,s,n),} kde q ∈ Q {\displaystyle q\in Q} , s ∈ Γ ∗ {\displaystyle s\in \Gamma ^{*}} a n ∈ N 0 {\displaystyle n\in \mathbb {N} _{0}} ; někteří autoři dávají přednost kompaktnímu zápisu s 1 q s 2 {\displaystyle s_{1}qs_{2}} , kde s 1 ∈ Γ ∗ {\displaystyle s_{1}\in \Gamma ^{*}} je úsek pásky vlevo od hlavy a s 2 ∈ Γ ∗ {\displaystyle s_{2}\in \Gamma ^{*}} je úsek pásky začínající políčkem, na kterém stojí hlava; v tomto případě musí být množiny stavů a symbolů na pásce disjunktní, tj. Q ∩ Γ = ∅ . {\displaystyle Q\cap \Gamma =\emptyset .}
Počáteční konfigurace Turingova stroje pro vstup w ∈ Γ ∗ {\displaystyle w\in \Gamma ^{*}} je konfigurace ( q 0 , w , 0 ) {\displaystyle (q_{0},w,0)} , resp. v kompaktním zápisu q 0 w {\displaystyle q_{0}w} .
Na začátku výpočtu je Turingův stroj v počáteční konfiguraci a na pásce je zapsané vstupní slovo. Dále pracuje v jednotlivých krocích:
TS můžeme chápat jako primitivní počítač, jehož program je dán přechodovou funkcí, vstupní data jsou tvořena vstupním slovem, páska slouží jako paměť a výstupem je pouze informace, zda výpočet skončil úspěchem, neúspěchem nebo pokračuje; alternativně můžeme za výsledek považovat i obsah pásky v okamžiku ukončení výpočtu. Existují různé modely TS, za dodržení určitých zásad (především možnost použití nekonečně velké pásky) jsou však co do možností výpočtů ekvivalentní (i když se mohou výrazně lišit efektivitou). Samotný TS pracuje podobně jako konečný automat, ale s tím zásadním rozdílem, že má nekonečně velkou pásku, na kterou může zapisovat a libovolně se po ní může pohybovat. Na začátku definujeme stavy a přechodové funkce, přijímající a zamítající stav a abecedu, nad kterou TS pracuje. Poté na pásku TS vložíme slovo, které se skládá ze symbolů abecedy, a TS se pokusí toto slovo přijmout. Při tom můžeme po TS požadovat, aby nějakým způsobem pozměnil symboly na pásce, čímž můžeme realizovat nějaký výpočet, například násobení. TS může výpočet ukončit dvěma způsoby: buď dojde do koncového stavu (což interpretujeme, že TS vstupní slovo w přijal, akceptoval), nebo se dostane do situace, kdy pro danou kombinaci stavu a vstupního symbolu není definována žádná činnost (což interpretujeme jako odmítnutí vstupního slova w). Třetí možností je, že výpočet TS nikdy neskončí.
Popíšeme jednoduchý TS, který bude mít za úkol zjistit, zda slovo napsané na pásku má tvar 0n1n, n>0 (zda řetězec obsahuje n nul a následně n jedniček). Víme, že konečný automat tento problém řešit neumí, ale zásobníkový automat to již řešit umí. Ukážeme, jak by vypadal TS M, který by tento problém vyřešil.
Základní myšlenkou algoritmu je, že náš Turingův stroj vždy vymaže počáteční 0, přesune hlavu na konec slova, vymaže z něj poslední 1 a přesune se zpět na začátek slova. Tuto činnost opakuje, dokud slovo nevymaže nebo se nedostane do stavu, kdy další krok není definován (v tom případě slovo do jazyka nepatří).
Formální zápis: Q = { q 0 , q 1 , q 2 , q 3 , q 4 , q 5 , q 6 , q 7 } {\displaystyle Q=\{q_{0},q_{1},q_{2},q_{3},q_{4},q_{5},q_{6},q_{7}\}} , Γ = { 0 , 1 , b } {\displaystyle \Gamma =\{0,1,b\}} , počáteční stav je q 0 {\displaystyle q_{0}} a F = { q 7 } {\displaystyle F=\{q_{7}\}} . Přechodová funkce by vypadala následovně:
Několik poznámek k algoritmu:
Ukázka výpočtu výše uvedeného Turingova stroje při vstupu 0011. Zapíšeme ji jako posloupnost po sobě následujících konfigurací. Nad šipkou představující jeden krok výpočtu je vždy uvedeno číslo použitého pravidla: q 0 0011 {\displaystyle q_{0}0011} → 1 {\displaystyle {\overset {1}{\rightarrow }}} q 1 011 {\displaystyle q_{1}011} → 2 {\displaystyle {\overset {2}{\rightarrow }}} 0 q 1 11 {\displaystyle 0q_{1}11} → 3 {\displaystyle {\overset {3}{\rightarrow }}} 01 q 2 1 {\displaystyle 01q_{2}1} , → 4 {\displaystyle {\overset {4}{\rightarrow }}} 011 q 2 {\displaystyle 011q_{2}} → 5 {\displaystyle {\overset {5}{\rightarrow }}} 01 q 3 1 {\displaystyle 01q_{3}1} → 6 {\displaystyle {\overset {6}{\rightarrow }}} 0 q 4 1 {\displaystyle 0q_{4}1} → 8 {\displaystyle {\overset {8}{\rightarrow }}} q 5 01 {\displaystyle q_{5}01} → 10 {\displaystyle {\overset {10}{\rightarrow }}} q 6 b 01 {\displaystyle q_{6}b01} → 12 {\displaystyle {\overset {12}{\rightarrow }}} q 0 01 {\displaystyle q_{0}01} → 1 {\displaystyle {\overset {1}{\rightarrow }}} q 1 1 {\displaystyle q_{1}1} → 3 {\displaystyle {\overset {3}{\rightarrow }}} 1 q 2 {\displaystyle 1q_{2}} → 5 {\displaystyle {\overset {5}{\rightarrow }}} q 3 1 {\displaystyle q_{3}1} → 6 {\displaystyle {\overset {6}{\rightarrow }}} q 4 b {\displaystyle q_{4}b} → 7 {\displaystyle {\overset {7}{\rightarrow }}} q 7 b {\displaystyle q_{7}b} . Turingův stroj dospěl do koncového stavu, výpočet končí úspěšně.
0011
Ukázka výpočtu při vstupu 001: q 0 001 {\displaystyle q_{0}001} → 1 {\displaystyle {\overset {1}{\rightarrow }}} q 1 01 {\displaystyle q_{1}01} → 2 {\displaystyle {\overset {2}{\rightarrow }}} 0 q 1 1 {\displaystyle 0q_{1}1} → 3 {\displaystyle {\overset {3}{\rightarrow }}} 01 q 2 {\displaystyle 01q_{2}} , → 5 {\displaystyle {\overset {5}{\rightarrow }}} 0 q 3 1 {\displaystyle 0q_{3}1} → 6 {\displaystyle {\overset {6}{\rightarrow }}} q 4 0 {\displaystyle q_{4}0} . Ale δ ( q 4 , 0 ) {\displaystyle \delta (q_{4},0)} není definováno, výpočet končí neúspěšně. Do stavu q 4 {\displaystyle q_{4}} se náš stroj dostane po vymazání poslední 1. U slova 0 n 1 n {\displaystyle 0^{n}1^{n}} mohou v této situaci nastat jen dva případy – buď se tím slovo vyprázdnilo (instrukce 7) nebo ještě jeho část zbývá, ta ale musí končit jedničkami (instrukce 8).
001
Ukázka výpočtu při vstupu 0101: q 0 0101 {\displaystyle q_{0}0101} → 1 {\displaystyle {\overset {1}{\rightarrow }}} q 1 101 {\displaystyle q_{1}101} → 3 {\displaystyle {\overset {3}{\rightarrow }}} 1 q 2 01 {\displaystyle 1q_{2}01} . Výpočet opět končí neúspěšně, protože přechodová funkce δ ( q 2 , 0 ) {\displaystyle \delta (q_{2},0)} není definována. V korektním slově řetězec jedniček zahájený za první nulou pokračuje až do konce slova, ve stavu q 2 {\displaystyle q_{2}} tedy stroj může na pásce najít jen symboly 1 nebo b {\displaystyle b} .
Existují různé modifikace Turingova stroje, které mají stejnou sílu, tzn. že přijímají stejnou třídu jazyků jako základní model.
Tvorba složitějších TS není jednoduchá záležitost a subrutiny slouží k usnadnění této činnosti. Subrutina je množina stavů, která obsahuje počáteční a koncový stav. Zpravidla řeší nějaký dílčí problém v TS. V běžném programování má ekvivalent ve funkci, která také má nějaké vstupní a výstupní stavy a řeší zpravidla nějaký dílčí problém celého programu. V předchozím příkladě kontroly 0n1n by mohla být subrutina například vrácení hlavy na začátek nebo kontrola, zda už máme označena všechna čísla.
Každý Turingův stroj můžeme zakódovat do jednoho řetězce. Existuje nekonečně mnoho způsobů, jak TS zakódovat, záleží na vlastní definici. Můžeme například očíslovat všechny stavy, očíslovat všechny symboly z abecedy a očíslovat všechny směry, kterými se může hlava vydat. Následně můžeme přechodovou funkci (q1,a2)→(q3,t6,L1), kde dolní indexy označují očíslování, zakódovat takto: 01001000100000010. Jako oddělovač jsme použili jedničku a počet nul se vždy rovná očíslování daného objektu. Více různých přechodových funkcí můžeme oddělit dvěma jedničkami, takže pokud bychom přidali ještě funkci (q1,b3)→(q3,t6,R2), získali bychom řetězec 01001000100000010110100010001000000100. Takto můžeme zakódovat všechny přechodové funkce. Zbývá už jen označit startovací, přijímající a ukončovací stav, což můžeme udělat například tak, že startovací stav bude mít vždy index jedna, přijímající dva a zamítající tři. Do tohoto formátu pak můžeme zakódovat libovolný TS. Důsledkem tohoto faktu je, že množina všech Turingových strojů je spočetná.
01001000100000010
01001000100000010110100010001000000100
Univerzální Turingův stroj je takový TS U, který jako vstup přijímá kód jiného TS T (jeho Gödelovo číslo) a vstupní slovo stroje T. Dokáže rozkódovat přechodovou funkci stroje T a výpočet tohoto stroje simulovat. Univerzální TS dokáže vypočítat libovolnou částečně rekurzivní funkci (neboli je ekvivalentní k univerzální částečně rekurzivní funkci), rozhoduje jakýkoliv rekurzivní jazyk a přijímá libovolný rekurzivně spočetný jazyk.
Univerzální jazyk Lu je takový jazyk, který obsahuje dvojice TS a řetězec, přičemž tento TS daný řetězec přijímá. Platí: Lu = {[M, w]| TS M přijímá slovo w}. Tento jazyk je rekurzivně spočetný. Abychom zjistili, zda daná dvojice patří do tohoto jazyka, potřebovali bychom sestrojit TS Tu, který by rozhodoval problém, zda TS M přijímá slovo w. K tomu budeme potřebovat Univerzální TS. Tento TS Tu bude postupovat tak, že na vstupu vezme libovolný (zakódovaný) TS M a slovo w a nasimuluje činnost TS M při vstupu w. Neexistuje jiná možnost jak zjistit, jestli TS M přijímá slovo w než ta, že prostě do TS M to slovo w dáme a počkáme, jak nám TS M odpoví. Takže Universální TS Tu nasimuluje činnost TS M a pokud TS M slovo přijme, i Tu dvojici přijme. Pokud TS M slovo odmítne, i Tu dvojici odmítne. Problém nastane, když se TS M při vstupu w zacyklí. Neexistuje postup, jakým by měl TS Tu poznat, že se vnitřní TS M zacyklil, takže se zacyklí taktéž. Náš Tu je tudíž schopen rozpoznat a přijmout všechny dvojice, které do jazyka Lu patří, ale není schopen identifikovat všechny dvojice, které do jazyka Lu nepatří. A to v případě, kdy se TS M při vstupu w zacyklí. Přičemž takový TS jistě existuje, například TS, který se zacyklí pro jakýkoliv vstup.
Některé problémy (Turingovy stroje) můžeme redukovat na jiné, což nám může pomoci při hledání důkazu, zda daný jazyk je rekurzivní jazyk, rekurzivně spočetný jazyk nebo zda není ani rekurzivně spočetný jazyk. Modelová situace: Víme, že jazyk A je rekurzivně spočetný (existuje důkaz). Chceme zjistit, zda je jazyk B rekurzivní, přičemž platí, že pokud by pro B existoval rozhodovací algoritmus, byli bychom s jeho pomocí schopni rozhodnout také problém A, tedy z A by se rázem stal rekurzivní jazyk. My ale víme, že A není rekurzivní jazyk, proto ani B nemůže být rekurzivní jazyk. Konkrétní příklad:
Jazyk HALT představuje množinu všech dvojic TS a slovo, pro které platí, že TS pro dané slovo zastaví (buď přijme, nebo odmítne, ale nezacyklí se). Tento jazyk není rekurzivní, protože pokud by tento jazyk byl rekurzivní, byli bychom schopni sestavit algoritmus, který by rozhodoval Univerzální jazyk Lu. TS, který by rozhodoval Lu by mohl vypadat následovně (předpokládáme vstup TS M a w, TSHALT je TS, který rozhoduje jazyk HALT):
Tento TS by byl schopný identifikovat, zda TS M přijímá slovo w. Nejdříve zjistí, zda daný TS M pro slovo w zastaví. Pokud se zacyklí, slovo rovnou odmítne. V opačném případě simuluje činnost M, který už musí vrátit nějaký výsledek, nemůže se zacyklit, to by bylo v rozporu s rozhodnutím TSHALT. Tento TS by tudíž rozhodoval Lu. My ale víme, že to není možné. A pokud není možné rozhodnout Lu, ale pomocí TSHALT by to šlo, znamená to, že HALT nemůže být rekurzivní jazyk.
Jazyk EMPTY je množina všech TS, které přijímají prázdný jazyk. Tento jazyk není rekurzivní, protože s jeho pomocí lze řešit Lu. Představme si TSEMPTY, který by rozhodoval, zda TS přijímá prázdný jazyk. Nyní sestrojíme TS, který je schopen rozhodnout Lu při vstupu TS M a slova w:
Protože ale víme, že jazyk Lu není rozhodnutelný, nemůže ani jazyk EMPTY být rozhodnutelný/rekurzivní.
Přestože je TS mocný nástroj, nedokáže vyřešit všechny problémy. Každý Turingův stroj můžeme zakódovat do řetězce skládajícího se z jedniček a nul podobně jako se kódují programy na reálných počítačích. Tím máme dokázáno, že Turingových strojů je spočetně mnoho, protože uzávěr nad abecedou {0, 1}* můžeme uspořádat do nekonečné posloupnosti 0, 1, 01, 10, 100, 101, 110, 111, 1000, ... a každý Turingův stroj zakódovaný do jedniček a nul tudíž v této posloupnosti nalezneme. Teď dokážeme, že jazyků (= problémů) je nespočetně mnoho. Použijeme k tomu diagonální metodu. Jazyk je vybraná podmnožina z uzávěru nad abecedou. Předpokládejme abecedu A={0,1} a jazyky (- značí, že daný jazyk slovo v prvním řádku neobsahuje, + značí, že obsahuje):
{0, 1}*
A={0,1}
-
+
A* = 0, 1, 01, 10, 100, 101, ... L1 = - - + + - + ... L2 = + - - - - + ... L3 = + + + + + - ... ...
Takže platí, že L1 = {0, 1, 01, 10, 100, 101, ...}, tedy L1 = {01, 10, 101, ...} atd. Nyní sestrojíme jazyk, který jistě nenajdeme v předchozím (nekonečném) výčtu jazyků. Nový jazyk Ln sestrojíme tak, že pokud jazyk Li obsahuje slovo z A* na i-té pozici, jazyk Ln ho obsahovat nebude a naopak. Tak jistě docílíme toho, že nový jazyk Ln se bude lišit alespoň v tomto jednom slově. Takže L1 neobsahuje slovo na prvním místě, tedy slovo 0, takže Ln ho obsahovat bude. Jazyk L2 neobsahuje slovo na druhém místě, 1, takže Ln ho bude obsahovat. Jazyk L3 obsahuje 01, takže Ln ho nebude obsahovat. A tak dále. Ln={0, 1, 01,...}. Jazyk Ln nenajdeme v předchozím výčtu jazyků, protože s každým uvedeným jazykem Li se bude lišit alespoň v tom, zda (ne)obsahuje slovo z A* na i-té pozici. Tím jsme dokázali, že množina všech jazyků (problémů) je nespočetná.
L1 = {0, 1, 01, 10, 100, 101, ...}
L1 = {01, 10, 101, ...}
0
1
Ln={0, 1, 01,...}
Pokud existuje nespočetně mnoho jazyků (problémů) a spočetně mnoho Turingových strojů, musí nutně existovat problémy, které Turingovy stroje nejsou schopné řešit. Jazyků (problémů) je zkrátka více než Turingových strojů.
typ 0
—
typ 1
typ 2
typ 3
frázová
bez zvláštního názvu
kontextová, monotonní
indexovaná
stromová apod.
bezkontextová
deterministická bezkontextová
regulární (výraz)
rekurzivně spočetný
rekurzivní
kontextový
indexovaný
částečně kontextový
bezkontextový
deterministický bezkontextový
viditelný zásobník, vkládané slovo
regulární
bezhvězdičkový, neiterativní
Turingův stroj
rozhodovač, zaručeně skončí pro libovolný vstup
lineárně ohraničený Turingův stroj
vnořený zásobník
vložený zásobník
zásobníkový automat, nedeterministický
deterministický zásobníkový automat
viditelný zásobník, pro vkládané slovo
konečný automat
neperiodický konečný automat