Wprowadzenie
Zadaniem tego modułu jest wprowadzić podstawowe narzędzie, jakim dla teorii złożoności jest maszyna Turinga. Jest to pewien abstrakcyjny model maszyny liczącej, który ma trzy podstawowe własności:
- jest prosty koncepcyjnie.
- potrafi przeprowadzić dowolne obliczenie możliwe na innych znanych nam maszynach.
- zużycie zasobów podczas obliczenia dobrze modeluje rzeczywiste komputery.
Pierwsza własność jest bardzo istotna, gdyż pozwala formułować i dowodzić twierdzenia dotyczące maszyn używając tylko najprostszych pojęć matematycznych.
Druga własność jest związana ze słynną tezą Churcha mówiącą, że każdą funkcję, którą da się efektywnie obliczać, da się obliczać używając maszyny Turinga. Jest to stwierdzenie natury filozoficznej i nie może być matematycznie udowodnione, ale rzeczywicie żaden ze znanych sposobów reprezentowania obliczeń nie potrafi przedstawić nic więcej niż maszyna Turinga.
Trzecia własność jest najważniejszą dla teorii złożoności. W dalszej części kursu wykażemy, że zarówno czas, jak i pamięć zużyta podczas obliczenia maszyny Turinga nie różnią się bardzo od zasobów zużywanych podczas wykonania algorytmów na rzeczywistych komputerach. Sprawia to, że wyniki teorii złożoności mówiące o obliczeniach na maszynach Turinga pozostają w ścisłym związku z rzeczywistymi problemami informatyki i praktycznymi implementacjami algorytmów.
Notacje i pojęcia wprowadzone w tym module będą szeroko wykorzystywane w następnych. Dlatego nawet osoby dobrze znające pojęcia związane z maszynami Turinga powinny sprawdzić, czy znane im definicje są tymi samymi, które będą używane w dalszej części kursu.
Model obliczeń
Zacznijmy od odpowiedzi na pytanie, po co model obliczeń jest nam w ogóle potrzebny?
Celem tego przedmiotu jest zbadanie jak złożone są różne problemy. Ale złożone dla kogo?
Dla komputera oczywiście, bo to właśnie problemy rozwiązywane przez komputery nas interesują.
Odpowiedź ta jest jak najbardziej poprawna, ale bardzo mało precyzyjna, bo dla jakiego komputera?
Na świecie są miliony, jeżeli nie miliardy, komputerów i różnią się one od siebie możliwościami. Zadania, które wykonuje się łatwo na jednym z nich mogą być trudne, lub wręcz niemożliwe, do wykonania na innym.
Moglibyśmy wybrać jakiś konkretny komputer, albo nawet model komputera -
powiedzmy „ZX Spectrum” (zakładając, że wszystkie jego egzemplarze pracują tak samo), ale niewiele by nas to posunęło do przodu -
dopiero po zbadaniu i dokładnym opisaniu możliwości naszego komputera moglibyśmy zacząć budować teorię obliczeń na nim.
Taki matematyczny opis działania komputera to właśnie model obliczeń.
W przypadku „ZX Spectrum” opis działania komputera musiałby uwzględniać organizację pamięci operacyjnej, strukturę procesora i jego operacje, oraz funkcje wejścia/wyjścia.
To nie jest coś co można opisać łatwo, nawet w wypadku prostego komputera. Opis współczesnego komputera opartego o najnowocześniejsze rozwiązania technologiczne byłby jeszcze trudniejszy.
Pokazuje to, ze wybór „ZX Spectrum” na model obliczeń nie jest najszczęśliwszy. To dlatego, że nie jest on koncepcyjnie prosty. Dlatego musimy poszukiwać jakiegoś łatwiejszego.
Maszyna Turinga
Maszyna Turinga została zaproponowana jako model „dowolnego możliwego obliczenia” przez Alana Turinga w 1936 roku. Czyli na całe dekady przed pojawieniem się pierwszego urządzenia, które moglibyśmy współcześnie nazwać komputerem. Powstała ona jako notacja do wyrażania algorytmów i służyła (głównie logikom) do określenia, jakie problemy są możliwe do algorytmicznego rozwiązania.
Maszyna Turinga jest bardzo prostym modelem komputera, który składa się z:
- nieograniczonej taśmy podzielonej na komórki, w których są zapisane zrozumiałe dla maszyny symbole.
- głowicy, która może się przesuwać po taśmie oraz odczytywać i zapisywać na niej symbole.
- mechanizmu sterującego, który może być w różnych stanach (ale skończenie wielu), który decyduje o działaniu maszyny. Na podstawie bieżącego stanu i symbolu znajdującego się pod głowicą podejmuje decyzje o:
- zmianie zawartości komórki taśmy znajdującej się pod głowicą.
- przesunięciu głowicy w lewo lub w prawo. Ponieważ taśma jest nieograniczona (czyli potencjalnie nieskończona), zawsze można wykonać przesunięcie.
- zmianie stanu mechanizmu sterującego.
Zaskakująco prosty opis maszyny liczącej w porównaniu do tego, który musielibyśmy napisać, żeby opisać działanie „ZX Spectrum”.
Definicja formalna
Aby móc stworzyć formalną teorię obliczeń na maszynach Turinga potrzebujemy matematycznego opisu. Jest wiele różnych definicji formalnych i każda z nich mogłaby konkurować o miano najlepszej. Musimy jednak wybrać którąś z nich. W związku z tym proponujemy następującą:
Definicja 1
Maszyna Turinga
, to siódemka: M =
, gdzie:
to skończony zbiór stanów maszyny.
to skończony zbiór symboli wejściowych, które służą do reprezentacji wejścia maszyny i są zapisane na taśmie przed rozpoczęciem obliczenia.
to skończony zbiór symboli taśmowych - są to wszystkie symbole jakie mogą się znajdować na taśmie. Oczywiście
.
to symbol pusty - specjalny symbol taśmowy nie należący do symboli wejściowych. Symbol pusty będzie zawsze wypełniał prawie całą taśmę.
to funkcja przejścia. Jest to funkcja częściowa prowadząca z
w
. Wartość
oznacza, że maszyna będąca w stanie
i czytając symbol
z taśmy przejdzie do stanu
, zapisze symbol
na taśmie i przesunie głowicę o jedną komórkę w lewo (
) lub w prawo (
).
to stan początkowy w jakim znajduje się maszyna przed rozpoczęciem obliczenia.
to zbiór stanów akceptujących będący podzbiorem
. Obliczenie maszyny kończy się kiedy osiągnięty zostanie któryś ze stanów
.
Działanie
Jak zatem uruchomić formalnie zdefiniowaną maszynę Turinga?
Najpierw trzeba odpowiednio przygotować taśmę - można rozważać różne reprezentacje wejścia, ale w dalszych rozważaniach będziemy zakładać, że taśma jest wypełniona symbolami pustymi
oprócz skończonego spójnego fragmentu, na którym znajduje się słowo wejściowe składające się z symboli wejściowych.
Następnie należy ustawić głowicę nad którąś z komórek taśmy - tu znowu można zastosować różne podejścia, ale dla wygody będziemy ustawiać głowicę nad pierwszą komórką słowa wejściowego. Pozostaje już tylko ustawić mechanizm sterujący na stan
i uruchomić maszynę.
Maszyna będzie działać zgodnie z algorytmem zapisanym w funkcji
- będzie zmieniać swój stan, przesuwać głowicę i zmieniać zawartość taśmy. Tak będzie sią działo dopóki nie nastąpi jeden z dwóch warunków:
- Maszyna znajdzie się w stanie akceptującym. Mówimy wtedy, że obliczenie zakończyło się akceptująco.
- Maszyna znajdując się w stanie
przeczyta z taśmy symbol
, a wartość funkcji częściowej
nie będzie zdefiniowana. Mówimy wtedy, że obliczenie zakończyło się błędem.
Może się również zdarzyć, że nigdy żaden z tych dwóch warunków nie nastąpi. W takim wypadku maszyna nie zakończy obliczenia.
Reprezentacja
Zanim przyjrzymy się przykładom konkretnych maszyn Turinga realizujących konkretne zadania przedstawimy dwa sposoby reprezentacji maszyn.
Pierwszy polega na przedstawieniu tabelki funkcji
. Możemy po prostu zapisywać w kolejnych wierszach ciągi
oznaczające
. Zazwyczaj nie jest wtedy potrzebne dokładne definiowanie pozostałych części maszyny. Symbolami taśmowymi są po prostu symbole występujące w tabelce, a dla symbolu pustego używa się standardowej notacji
. To, które symbole są symbolami wejściowymi też zazwyczaj wynika z kontekstu i nie budzi wątpliwości. Dla oznaczenia stanów akceptujących można użyć pogrubienia. Jedynie określenie stanu początkowego wymaga dodatkowego komentarza do tabelki.
Można też przedstawić maszynę w wygodny sposób na diagramie. Każdy ze stanów maszyny reprezentujemy rysując prostokąt z nazwą stanu zapisaną wewnątrz prostokąta. Dla przejrzystości oznaczamy stany końcowe przez pogrubienie nazwy stanu.
Następnie łączymy prostokąty strzałkami etykietowanymi trzema symbolami - dwoma symbolami taśmowymi i symbolem
lub
. Strzałka etykietowana
prowadząca od stanu
do stanu
oznacza
.
Przykład 2 [Maszyna dodająca dwie liczby w systemie unarnym]
Diagram maszyny dodającej dwie liczby
Możemy teraz przedstawić pierwszą maszynę Turinga. Będzie ona dodawać dwie liczby zapisane w systemie unarnym. Liczba naturalna
jest reprezentowana w systemie unarnym przez
jedynek zapisanych na taśmie obok siebie.
Zakładamy, że na wejściu maszyny znajdą się dwie liczby naturalne
i Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle m}
zapisane unarnie oddzielone pojedynczym zerem. Po zakończeniu działania maszyny na taśmie powinna być zapisana liczba Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle n+m}
. W związku z tym, językiem wejściowym maszyny będzie Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle \Sigma = \{0,1\}}
. Maszyna będzie miała 4 stany Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle Q = \{A,B,C,D\}}
, stanem początkowym będzie Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle A}
, a jedynym stanem akceptującym będzie Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle D}
.
Język maszyny. Języki rekurencyjne i rekurencyjnie przeliczalne
Maszyna z przykładu wykonywała bardzo pożyteczne zadanie. Dla określonego wejścia po zakończeniu działania pozostawiała na taśmie wynik dodawania. Można się domyślać, że inne działania arytmetyczne również można zakodować w algorytmie maszyny Turinga. Zachęcamy do zaprojektowania takiego kalkulatora. Czasem jednak jest całkiem nieistotne co maszyna pozostawia na taśmie, a interesuje nas tylko czy i w jaki sposób kończy swoje działanie.
Definicja 3
Językiem maszyny Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle M}
nazwiemy Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle L(M) \subseteq {\Sigma^\ast}}
zbiór tych słów wejściowych, dla których obliczenie maszyny Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle M}
kończy się w stanie akceptującym. Będziemy mówić, że Parser nie mógł rozpoznać (SVG (MathML może zostać włączone przez wtyczkę w przeglądarce): Nieprawidłowa odpowiedź („Math extension cannot connect to Restbase.”) z serwera „https://wazniak.mimuw.edu.pl/api/rest_v1/”:): {\displaystyle M}
rozpoznaje język
.
Definicja 4
Dlaczego język
jest tylko częściowo rozstrzygalny? Ponieważ jeżeli słowo
, to da się to sprawdzić - należy uruchomić maszynę
na słowie
i zostanie ono zaakceptowane. Natomiast jeżeli
, to niekoniecznie da się to sprawdzić. Możemy oczywiście uruchomić
na
, ale jeżeli to obliczenie nigdy się nie kończy, to nigdy nie dowiemy się czy
akceptuje
.
Żeby uchwycić własność pełnej rozstrzygalności definiujemy własność stopu dla maszyny Turinga.
Definicja 5
Własności i różnice pomiędzy językami rozstrzygalnymi i częściowo rozstrzygalnymi są bardzo ciekawe, ale świadomie nie będziemy się nimi tu zajmować. Prawie wszystkie języki z którymi się spotkamy w czasie tego kursu będą rozstrzygalne.
Przykład 6 [Maszyna rozpoznająca język
]
Diagram maszyny rozpoznającej język

Przedstawimy teraz drugą maszynę Turinga, której zadaniem jest rozpoznanie konkretnego języka:
gdzie
oznacza słowo
z odwróconą kolejnością liter.
Specyfikacja problemu narzuca język wejściowy
. Maszyna, którą zaprezentujemy będzie miała 7 stanów
. Stan
będzie stanem początkowym, a jedynym stanem akceptującym będzie stan
.
Ćwiczenia
Ćwiczenie 7 [Język maszyny przykładowej
Udowodnij, że maszyna z przykładu rzeczywiście rozpoznaje język
.
Podpowiedź
Przeprowadź dowód indukcyjny ze względu na długość słowa wejściowego.
Rozwiązanie
Zauważmy, że jedynym stanem akceptującym jest stan
, a jedyne przejście do stanu
pochodzi ze stanu
przy symbolu
. Ponieważ stan
jest stanem początkowym, oznacza to, że aby maszyna zaakceptowała niepuste słowo
musi ona powrócić jeszcze choć raz do stanu
. Przyglądając się ścieżkom, które umożliwiają powrót do stanu
dochodzimy do wniosku, że maszyna
akceptuje słowa postaci:
jest słowem pustym.
lub
, gdzie
akceptuje słowo
.
Przeprowadzimy teraz dowód indukcyjny ze względu na długość słowa
, że
należy do języka maszyny wtedy i tylko wtedy, gdy
dla pewnego
.
Dla długości równej
teza jest trywialna. Krok indukcyjny dowodzimy dla
o długości większej od zera. Słowo
należące do języka maszyny nie jest zatem słowem pustym i jest postaci
lub
, a
jest postaci
. To z kolei jest równoważne
lub
.
Ćwiczenie 8 [Mnożenie liczb unarnych]
Skonstruuj maszynę analogiczną do maszyny dodającej, która wykona mnożenie dwóch liczb zapisanych unarnie.
Podpowiedź
Skonstruuj maszynę odejmującą jeden od liczby zapisanej unarnie, a następnie połącz ją z maszyną dodającą.
Rozwiązanie
Przedstawimy maszynę, która po kolei zrealizuje etapy obliczenia:
1. skróci zapis pierwszej liczby o jedną jedynkę i przejdzie na koniec wejścia:
2. dopisze reprezentację liczby
, czyli jedną jedynkę za wejściem i przejdzie z powrotem na początek wejścia:
3. dopóki pierwsza liczba wejścia nie zostanie całkiem zamazana będzie zamazywać jedną komórkę i przechodzić do drugiej liczby:
4. i dopisywać ją do wyniku:
5. a jeżeli liczba jest już zamazana, to usunie też drugą liczbę i pozostawi sam wynik:
Ćwiczenie 9 [Źle ustawiona głowica]
Załóżmy, że głowica nie byłaby ustawiana na początku słowa wejściowego, tylko w dowolnym miejscu na taśmie. Pokaż, że można napisać program maszyny Turinga, który „znajdzie” początek słowa wejściowego i ustawi tam głowicę.
Załóż, że maszyna pracuje nad alfabetem
oraz, że słowo wejściowe jest niepuste.
Podpowiedź
Gdyby było wiadomo, że słowo znajduje się „na lewo”, lub „na prawo” od pozycji początkowej głowicy, to wystarczyłoby skierować głowicę w odpowiednim kierunku. Ponieważ nie masz takiej informacji, to musisz przeglądać taśmę w obu kierunkach jednocześnie.
Podpowiedź
Dodaj dodatkowy symbol do alfabetu
, którego użyjesz jako znacznika miejsc odwiedzonych w poszukiwaniu słowa wejściowego. Nie zapomnij na końcu usunąć znaczników z taśmy!
Rozwiązanie
Przedstawimy maszynę, która używa dodatkowego symbolu
na oznaczenie komórek już odwiedzonych przez głowicę. Maszyna rozpoczyna w stanie
i poszerza odwiedzony obszar z lewej i z prawej strony aż znajdzie słowo wejściowe. Kiedy w końcu trafi na komórkę zawierającą
lub
, to usuwa wszystkie
z taśmy i ustawia się na początku słowa wejściowego w stanie
.
Ćwiczenie 10 [Obliczenie z „czystą” taśmą]
Pokaż, że jeżeli maszyna
rozpoznaje język
, to można ją zmodyfikować tak, żeby nowa maszyna
również rozpoznawała język
, ale po zakończeniu jej działania taśma była w całości wypełniona symbolem pustym.
Podpowiedź
Zmodyfikuj alfabet
tak, żeby dało się stwierdzić które komórki mogły być odwiedzone podczas obliczenia.
Rozwiązanie
Zauważmy, że po zakończeniu obliczenia
musimy zamienić wszystkie wystąpienia symboli innych niż
. Kłopot może sprawić to, że ciągi takich symboli mogą być oddzielone od siebie znakami pustymi i nie wiadomo, czy na lewo (lub na prawo) od danej pozycji są jeszcze symbole inne niż
.
Zmodyfikujemy teraz działanie maszyny
dodając nowy symbol taśmowy
, który będzie miał znaczenie „to jest symbol pusty zapisany na taśmie przez maszynę
”. Musimy zmienić funkcję
tak, żeby czytając znak
zachowywała się dokładnie tak samo, jak gdyby przeczytała
. Jednocześnie za każdym razem kiedy funkcja
wskazywała, że należy zapisać na taśmie symbol
, teraz należy zapisać symbol
.
Ta modyfikacja nie zmienia języka rozpoznawanego przez
, gdyż możemy patrzeć na wystąpienia symbolu
jak na wystąpienia
i wtedy całe obliczenie wygląda tak samo.
Musimy teraz „wykryć” zakończenie obliczenia maszyny i przejść do specjalnego stanu „czyszczącego-akceptującego” lub „czyszczącego-błędnego”. Wszystkie przejścia do stanów akceptujących modyfikujemy tak, żeby przechodziły do „czyszczącego-akceptującego”. Z kolei wszystkie niezdefiniowane przejścia ustawiamy tak, żeby prowadziły do stanu „czyszczącego-błędnego”.
Zauważmy, że dla nowej maszyny ciąg symboli różnych od
jest spójny - są to wszystkie komórki taśmy, nad którymi znajdowała się maszyna podczas obliczenia. Możemy w związku z tym odnaleźć jego początek (korzystając na przykład z maszyny zdefiniowanej w poprzednim ćwiczeniu, choć w tym wypadku zadanie jest łatwiejsze) i zamazać wszystkie symbole aż do końca.
Kiedy taśma jest już pusta możemy przejść do stanu akceptującego lub umyślnie wykonać błąd w zależności od tego, czy dokonaliśmy czyszczenia „akceptującego”, czy „błędnego”.
Ćwiczenie 11 [Suma i przecięcie języków rozstrzygalnych]
Pokaż, że jeżeli języki
i
są rozstrzygalne, to języki
i
są rozstrzygalne.
Rozwiązanie
Postępujemy zgodnie z podpowiedzią i używając maszyn z własnością stopu
i
rozpoznających odpowiednio
i
definiujemy nową maszynę
, która ma zasymulować uruchomienie obu maszyn na tym samym wejściu.
Alfabetem symboli taśmowych będzie
. Symbolem pustym jest teraz
.
Dokonujemy modyfikacji maszyn
i
, tak żeby po zakończeniu ich działania taśma była pusta oprócz jednej komórki zawierającej symbol 1 jeżeli słowo zostało zaakceptowane, lub 0 jeżeli zostało odrzucone (jest to prosta modyfikacja pomysłu wykorzystanego w poprzednim ćwiczeniu).
Następnie wprowadzamy dalsze modyfikacje tak, żeby maszyna
w swoim obliczeniu korzystała tylko z pierwszej współrzędnej alfabetu taśmowego. Po prostu produkcję
zamieniamy na zbiór produkcji postaci
po wszystkich
. Podobną modyfikację wykonujemy dla drugiej maszyny.
Wiemy, że maszyny
i
miały własność stopu. Ich modyfikacje nie zmieniły tego faktu. Możemy w związku z tym skonstruować maszynę
z własnością stopu, która po kolei:
- przepisuje litery słowa wejściowego
na pary
.
- wraca na początek słowa i uruchamia zmodyfikowaną wersję maszyny
.
- odszukuje początek słowa na drugich współrzędnych i uruchamia zmodyfikowaną wersję maszyny
.
- po zakończeniu obliczenia obu maszyn co najwyżej dwie komórki na taśmie są niepuste. Maszyna może osobno odczytać wynik na pierwszej współrzędnej i osobno na drugiej. W zależności od tego, czy
miało rozpoznawać sumę, czy przecięcie języków, maszyna przechodzi do stanu akceptującego gdy jeden, lub oba obliczenia pozostawiły po sobie 1 zapisaną na taśmie.
Zauważmy, że tej samej metody nie można zastosować do skonstruowania maszyny rozpoznającej sumę języków częściowo rozstrzygalnych. W takim przypadku obliczenie zmodyfikowanej maszyny
może się nigdy nie zakończyć dla słów należących do
. Aby rozwiązać takie zadanie będziemy musieli użyć innych technik.
Zapis programu i stanu maszyny
Bardzo ciekawą i istotną własnością jest to, że opis dowolnej maszyny Turinga można zapisać na taśmie.
Takiego kodowania maszyny na taśmie można dokonać na wiele różnych sposobów. Przyjrzymy się jednemu z nich.
Zauważmy, że dla działania maszyny nie są istotne „nazwy” elementów ze zbioru
i
. Możemy zatem założyć, że są to kolejne liczby naturalne. Wtedy do opisu maszyny wystarczy zapisać:
1.
- liczbę elementów w zbiorze
. Dalej zakładamy, że
.
2.
- liczbę elementów w zbiorze
.
3.
- liczbę elementów w zbiorze
. Wygodnie jest założyć, że numeracja w
pokrywa się z tą w
. Dzięki temu nie trzeba rozróżniać tych dwóch numeracji.
4.
- numer symbolu pustego.
5.
- liczbę zdefiniowanych wartości funkcji
.
6. Następnie
piątek liczb naturalnych
,
,
,
,
oznaczających, że
, gdy
.
, gdy
.
Możemy zatem zapisać te liczby w systemie unarnym oddzielając je pojedynczymi zerami i potraktować wynikowy napis jako słowo wejściowe nad alfabetem wejściowym
. Bardzo istotne jest, że słowo to ma skończoną długość - będziemy z tej skończonej reprezentacji nieraz korzystać.
Ćwiczenia
Ćwiczenie 12 [Konfiguracja maszyny]
Podobnie jak zapisaliśmy maszynę jako słowo wejściowe nad alfabetem
, możemy w podobny sposób zakodować opis sytuacji w jakiej znajduje się obliczenie maszyny w dowolnej chwili.
Przedstaw takie kodowanie tego opisu.
Podpowiedź
Dla zakodowania całego opisu obliczenia maszyny trzeba zapisać stan maszyny, zawartość taśmy i pozycję głowicy.
Zasoby potrzebne do obliczenia maszyny
Przy projektowaniu algorytmów komputerowych bardzo istotna jest ilość czasu i pamięci potrzebnych do ich wykonania. Dla maszyn Turinga można bardzo łatwo badać zużycie czasu i pamięci.
Definicja 13
Możemy też zdefiniować czas działania samej maszyny.
Definicja 14
Odpowiada to intuicji, że
jest równe czasowi wystarczającemu do obliczenia na dowolnym
-literowym słowie wejściowym. Zauważmy, że dla maszyny bez własności stopu nie można podać złożoności czasowej.
Wprowadzamy również podobne definicje dla pamięci wymaganej dla obliczenia związane z liczbą komórek taśmy użytych podczas obliczenia.
Definicja 15
Definicja 16
Ćwiczenia
Ćwiczenie 17 [Czas i pamięć potrzebna przykładowym maszynom]
Spróbuj określić złożoność czasową i pamięciową dla przykładowych maszyn - dodającej i rozpoznającej język
.
Podpowiedź
|
Złożoność czasowa |
Złożoność pamięciowa
|
Maszyna dodająca |


 |


|
Maszyna rozpoznająca  |

 |
|
Wielotaśmowa maszyna Turinga
Wielotaśmowa maszyna Turinga
Interesującym rozszerzeniem modelu maszyny Turinga jest dodanie do jej mechanizmu dodatkowych taśm.
-taśmowa maszyna Turinga różni się od zwykłej maszyny Turinga tym, że ma
niezależnych głowic, które poruszają się po różnych taśmach - każda z nich jest nieograniczona. W nowym modelu, mechanizm sterujący podejmuje decyzje na podstawie aktualnego stanu i odczytów ze wszystkich głowic. Dokonuje zapisów i przesunięć na każdej z taśm niezależnie. Co więcej, o ile w przypadku zwykłych maszyn Turinga wymagaliśmy przesunięcia głowicy, to teraz pozwalamy, żeby głowice pozostawały na swoich miejscach.
Żeby sformalizować sposób podawania wejścia do maszyny wielotaśmowej zakładamy, że słowo wejściowe jest zapisane na pierwszej taśmie, a pozostałe taśmy są w całości wypełnione symbolami pustymi. Natomiast definicje akceptacji i zakończenia obliczenia z błędem pozostają takie same jak w przypadku zwykłej maszyny Turinga.
Maszyna off-line
Maszyna off-line jest z kolei zawężeniem modelu maszyny wielotaśmowej. Modyfikacja polega na tym, że maszynie off-line nie wolno dokonywać zmian taśmy wejściowej. Rozważa się też maszyny off-line z taśmą wyjściową - wtedy dodatkowym ograniczeniem jest to, że głowica na ostaniej taśmie może tylko zapisywać symbole i przesuwać się tylko w prawo. Taki model odpowiada sytuacji w której program ma wejście i wyjście na osobnych taśmach, a pozostałe taśmy pełnią funkcję pamięci roboczej.
Złożoność
O ile definicje związane ze złożonością czasową zwykłej maszyny Turinga można bez zastrzeżeń stosować do maszyn wielotaśmowych, to definicje użytej pamięci wymagają pewnego komentarza. W przypadku zwykłej maszyny wielotaśmowej licząc liczbę komórek używanych podczas obliczenia sumujemy liczby użytych komórek na każdej z taśm.
W przypadku maszyn off-line do komórek używanych zaliczamy tylko komórki z taśm roboczych - nie uwzględniamy przy sumowaniu komórek taśmy wejściowej i wyjściowej (jeżeli taka istnieje). Takie podejście pozwala badać bardzo ciekawe obliczenia z pamięcią mniejszą niż rozmiar danych wejściowych.
Ćwiczenia
Ćwiczenie 18 [Definicje funkcji przejścia]
Sformalizuj definicję funkcji przejścia dla
-taśmowej maszyny Turinga w wersji normalnej i off-line.
Podpowiedź
Przyjrzyj się definicji zwykłej maszyny Turinga i zmodyfikuj jej argumenty i wynik tak, żeby można było wyrazić dostęp do
taśm. Który element jest niepotrzebny w wypadku maszyny off-line?
Rozwiązanie
Formalna definicja funkcji przejścia dla
-taśmowej maszyny Turinga musi zależeć od stanu i odczytu ze wszystkich
taśm i decydować o zmianie stanu i wszystkich
głowic. W związku z tym ma postać:
Wersja off-line nie może dokonywać zapisu na pierwszej taśmie, stąd funkcja przyjmuje postać:
Wersja off-line z taśmą wyjściową dodatkowo nie może dokonywać odczytów z ostatniej taśmy. W związku z tym funkcja przyjmuje postać:
Ćwiczenie 19 [Symulacja maszyny
-taśmowej]
Pokaż, że dla języka rozpoznawanego przez
-taśmową maszynę Turinga
o złożoności czasowej
da się skonstruować zwykłą maszynę Turinga
o złożoności czasowej
.
Podpowiedź
Wykorzystaj pomysł z ćwiczenia o sumie i przecięciu języków rekurencyjnych, aby zakodować zawartość
-taśm na jednej taśmie. Użyj kopii alfabetu taśmowego do zaznaczenia pozycji głowicy na każdej z taśm.
Rozwiązanie
Zakładamy, że maszyna
została zmodyfikowana tak jak w ćwiczeniu o „czystym” obliczeniu i używa symbolu
do oznaczenia komórek z symbolem pustym zapisanym podczas obliczenia. Pozwoli to określić które fragmenty taśmy zawierają „istotne” informacje.
Maszyna
będzie pracować przy alfabecie wejściowym
i taśmowym
. Gdzie
jest kopią alfabetu taśmowego, a symbol
oznacza, że na taśmie jest symbol
i głowica znajduje się nad tą właśnie komórką.
Stany Maszyny
to
. Stany z
będą odpowiadać stanom maszyny
, a stany
sytuacji w której maszyna
jest stanie
, a na taśmie
pod głowicą jest
. Symbolu
na
-tej pozycji używamy, kiedy jeszcze nie wiemy co jest pod głowicą na
-tej taśmie.
Maszyna
będzie działać w następujący sposób:
- przepisze wejście z wartości
na
w wypadku pierwszego symbolu i
w wypadku pozostałych.
- będąc w stanie odpowiadającym stanowi
maszyny
i na lewym końcu taśmy (do znalezienia lewego końca taśmy używamy symboli
i metody z ćwiczenia o ,,czystym obliczeniu)
- przejdzie do stanu
i przeglądnie zawartość taśmy aż do prawego końca uzupełniając informację w stanie. Tzn. kiedy napotkana zostanie komórka z symbolem
, to w stanie
wartość na
-tej współrzędnej stanu zmienia się z
na
.
- Posiadając już pełną informację o odczytach ze wszystkich taśm dokona jeszcze jednego przeglądnięcia taśmy w którym zostaną zmienione symbole taśmowe (na każdej ze współrzędnych osobno zgodnie z programem maszyny
.
Jeżeli maszyna
działa dla wejścia rozmiaru
w czasie
, to działanie maszyny
zakończy się po wykonaniu
pętli przeglądających całą używaną taśmę. Wykonanie pojedynczej pętli odbywa się w czasie proporcjonalnym do długości użytej części taśmy. Tą z kolei możemy ograniczyć do
komórek na lewo i
komórek na prawo od pozycji początkowej - gdyż żadna z głowic w czasie
nie może pokonać większej odległości. W związku z tym otrzymujemy oszacowanie
na złożoność czasową wynikowej maszyny.
Ćwiczenie 20 [Symulacja na maszynie off-line]
Pokaż jak zasymulować
-taśmową maszynę Turinga na
-taśmowej maszynie off-line.
Podpowiedź
Użyj rezultatu z poprzedniego ćwiczenia.
Rozwiązanie
Używając konstrukcji pozwalającej zasymulować
-taśmową maszynę Turinga na jednotaśmowej można całe obliczenie przeprowadzić na drugiej taśmie maszyny off-line. Wystarczy skopiować słowo wejściowej z pierwszej taśmy do odpowiednio przygotowanych komórek drugiej taśmy.
Niedeterministyczna maszyna Turinga
Najciekawszym i bardzo istotnym dla dalszej części wykładu rozszerzeniem modelu maszyny Turinga jest wprowadzenie niedeterminizmu do funkcji przejścia. Niedeterministyczna maszyna Turinga to taka, dla której mechanizm sterujący może przy konkretnym stanie i odczycie z taśmy postąpić na kilka różnych sposobów.
Definicja 21
Niedeterministyczna maszyna Turinga, to maszyna Turinga, w której funkcja przejścia jest częściową funkcją wielowartościową:
Na niedeterministyczną maszynę Turinga można patrzeć jak na automat zachowujący się losowo - podając słowo na wejście maszyny nie wiemy jakich wyborów dokona mechanizm sterujący, jeżeli będzie miał możliwość wykonania różnych przejść. Nie jest to jednak podejście najlepsze. Żeby zrozumieć różnicę podamy teraz definicję akceptacji słowa przez niedeterministyczną maszynę Turinga.
Definicja 22
Mówimy, że niedeterministyczna maszyna
akceptuje słowo
jeżeli istnieje taki ciąg przejść, który kończy się stanem akceptującym.
Taka definicja akceptacji powoduje, że zamiast traktować niedeterministyczne maszyny jak maszyny zachowujące się losowo, lepiej patrzeć na nie jak na maszyny, które sprawdzają wszystkie możliwości postępowania. To podejście pozwala lepiej wyobrazić sobie potencjał obliczeniowy, jaki jest ukryty w niedeterminizmie.
Pytanie o to jak dużą moc obliczeniową zyskuje się wprowadzając niedeterminizm jest kluczowym dla teorii złożoności i jest istotą sławnego problemu
, który omówimy dokładnie w dalszej części kursu.
Przykład 23 [Drzewo możliwych wykonań]
Drzewo możliwych wykonań z zaznaczonymi wykonaniami akceptującymi
Przyjrzyjmy się teraz diagramowi przykładowej niedeterministycznej maszyny Turinga. Można zauważyć, że niedeterminizm pojawia się w stanie

. Maszyna będąc w stanie

i czytając z taśmy symbol

może zarówno:
- pozostawić
na taśmie i przesunąć głowicę w prawo nie zmieniając stanu.
- zamienić
na
, przesunąć się w prawo i zmienić stan na
.
Podobnie reakcja maszyny na symbol
może być dwojaka.
Żeby zobrazować działanie niedeterministycznej maszyny Turinga można narysować drzewo możliwych wykonań. W korzeniu drzewa zapisujemy stan początkowy maszyny, a od każdego wierzchołka reprezentującego stan obliczenia rysujemy strzałkę do kolejnych możliwych stanów.
Na drzewie zaznaczyliśmy ścieżkę, która prowadzi do akceptacji kolorem zielonym, a obliczenia, które prowadzą do odrzucenia słowa na czerwono. Zauważmy, że jest tylko jedna ścieżka akceptująca i każde odejście od niej prowadzi do odrzucenia słowa.
Złożoność
Wprowadzenie definicji złożoności czasowej i pamięciowej dla niedeterministycznej maszyny Turinga nastręcza pewnych trudności. Jest kilka podejść do tego zagadnienia, a subtelne różnice są zazwyczaj pomijane w literaturze. My zdecydowaliśmy się na najbardziej wymagającą definicję.
Przede wszystkim definiujemy, że niedeterministyczna maszyna
zatrzymuje się na słowie
, kiedy żaden z możliwych scenariuszy obliczenia nie jest nieskończony.
Definicja 24
Zauważmy, że czas wykonania odpowiada wysokości drzewa możliwych wykonań.
Definicje miary złożoności czasowej i pamięciowej maszyny niedeterministycznej są analogiczne do tych dla zwykłych maszyn. Są one określone dla maszyn z własnością stopu i dla długości
są równe maksimum z czasu i pamięci potrzebnej do wykonania po wszystkich słowach wejściowych tej długości.
Ćwiczenia
Ćwiczenie 25 [Symulacja maszyny niedeterministycznej na maszynie deterministycznej]
Pokaż, że jeżeli język
jest rozpoznawany przez niedeterministyczną maszynę Turinga w czasie
, to istnieje deterministyczna maszyna Turinga rozpoznająca ten sam język w czasie
dla pewnej stałej
.
Podpowiedź
Skonstruuj wielotaśmową deterministyczną maszynę, która będzie przeglądać wszystkie ścieżki drzewa możliwych wykonań.
Rozwiązanie
Niech
. Zauważmy, że
jest maksymalnym stopniem rozgałęzienia, jaki może wystąpić w drzewie możliwych obliczeń maszyny.
Mając dany ciąg
o długości
liczb z przedziału
możemy zdeterminować działanie maszyny w następujący sposób: W
-tym kroku wykonania wybieramy
-te z dostępnych przejść. Jeżeli obliczenie kończy się w mniej niż
krokach to ciąg
wyznacza działanie maszyny. Jeżeli obliczenie nie kończy się w pierwszych
krokach, to będziemy musieli sprawdzić dłuższe ciągi
.
Nasza maszyna będzie
-taśmową maszyną deterministyczną. Pierwsza taśma będzie zawsze przechowywać słowo wejściowe. Druga taśma będzie zawierać ciąg determinujący obliczenie niedeterministyczne. Na trzeciej taśmie będzie się odbywać symulacja. Czwarta taśma z kolei będzie służyć do przechowywania specjalnego znacznika. Maszyna będzie działać w następujący sposób, że dla kolejnych liczb naturalnych
:
- usunie znaczniki z czwartej taśmy.
- przeglądnie wszystkie ciągi
długości
liczb z przedziału
zapisując je na drugiej taśmie i dla każdego z nich:
- skopiuje zawartość pierwszej taśmy na trzecią taśmę i uruchomi tam zdeterminowany przy pomocy ciągu
program niedeterministycznej maszyny.
- jeżeli obliczenie się nie zakończyło w ciągu pierwszych
kroków, to zaznaczy ten fakt zapisując specjalny symbol na czwartej taśmie.
- wyczyści trzecią taśmę używając techniki „czystego” obliczenia.
Jeżeli którekolwiek ze zdeterminowanych obliczeń zakończy się akceptacją, to maszyna akceptuje słowo wejściowe. Jeżeli dla jakiejś liczby
wszystkie obliczenia się zakończą (a więc nie będzie odpowiedniego znacznika na czwartej taśmie), to obliczenie powinno się zakończyć z błędem.
Tak skonstruowana maszyna rozpoznaje ten sam język co zadana maszyna niedeterministyczna. Jeżeli słowo
należy do języka, to ciąg wyborów
, który o tym świadczy zostanie w końcu przeglądnięty i słowo zaakceptowane. Należy zwrócić uwagę, że obliczenie nie zostanie wcześniej przerwane z błędem, gdyż prefiksy
zapewnią kontynuację obliczeń.
Jeżeli natomiast słowo
nie należy do języka, to przy
wszystkie możliwe scenariusze wykonania muszą się zakończyć i w związku z tym obliczenie zostanie przerwane z błędem.
Czas działania tak powstałej maszyny dla słowa wejściowego długości
możemy oszacować przez:
dla pewnej stałej
.
Możemy teraz użyć symulacji
-taśmowej maszyny Turinga działającej w czasie
na jednotaśmowej maszynie w czasie
, czyli
dla
.
Ćwiczenie 26 [Symulacja maszyny niedeterministycznej
-taśmowej na
-taśmowej]
Pokaż, że język rozpoznawany przez
-taśmową niedeterministyczną maszynę Turinga
w czasie
jest również rozpoznawany przez
-taśmową niedeterministyczną maszynę Turinga w czasie
.
Podpowiedź
Użyj niedeterminizmu do wylosowania obliczenia akceptującego na
-taśmowej maszynie, a następnie sprawdź, że wylosowałeś poprawną ścieżkę obliczeń.
Rozwiązanie
Niech
oznacza tak jak poprzednio
.
Maszyna
-taśmowa, którą skonstruujemy będzie działać w następujący sposób, że na drugiej taśmie zapisze ciąg grup :
symboli z
i jedna liczba z przedziału
. Znaczenie
-tej grupy postaci
jest takie, że w obliczeniu akceptującym maszyny
w
-tym kroku z
taśm są wczytane symbole
, a maszyna wybiera
-te z dostępnych przejść.
Zauważmy, że taki ciąg wyznacza jednoznacznie zachowanie maszyny - znamy stan początkowy, a każda grupa wyznacza następny stan maszyny.
Aby skonstruować naszą maszynę chcemy mieć możliwość sprawdzenia, czy taki arbitralny ciąg może rzeczywiście wystąpić. Możemy wykonać takie sprawdzenie symulując za każdym razem tylko jedną z
taśm na pierwszej taśmie konstruowanej maszyny
-taśmowej. Taka symulacja musi sprawdzić, czy rzeczywiście odczyt z
-tej taśmy w kroku
jest taki jak przewiduje to scenariusz zapisany na drugiej taśmie.
Widać już, że możemy skonstruować maszynę, która generuje przy pomocy przejść niedeterministycznych ciąg opisujący całe obliczenie, a następnie sprawdza, że jest to rzeczywiście możliwy ciąg odczytów z taśmy. Kiedy sprawdzone obliczenie prowadzi do stanu akceptującego, to maszyna akceptuje słowo wejściowe.
Jedyny problem jaki pozostał to metoda niedeterministycznego generowania ciągu grup. Ponieważ nie chcemy uzależniać metody od funkcji
zastosujemy następujące podejście, że będziemy generować po kolei ciągi o długości kolejnych potęg
. Zaczniemy od ciągu długości
. Potem za każdym razem jeżeli obliczenie kodowane przez poprzedni ciąg się nie zakończyło, ale ciąg był poprawny, to wydłużamy go dwukrotnie.
Takie podejście zapewnia, że o ile słowo
należało do języka maszyny, to obliczenie o tym świadczące zostanie odkryte przez naszą nową maszynę.
Jeśli natomiast
nie należy do języka, to żadne z obliczeń nie prowadzi do akceptacji, a kiedy długość testowanych ciągów przekroczy
to każde z obliczeń się kończy i dłuższe ciągi nie będą już testowane.
Czas działania nowej maszyny dla słowa długości
jest proporcjonalny do
Testy końcowe