Złożoność obliczeniowa/Wykład 1: Obliczenia w modelu maszyny Turinga: Różnice pomiędzy wersjami
m Zastępowanie tekstu – „<math> ” na „<math>” |
|||
(Nie pokazano 129 wersji utworzonych przez 8 użytkowników) | |||
Linia 5: | Linia 5: | ||
* potrafi przeprowadzić dowolne obliczenie możliwe na innych znanych nam maszynach. | * potrafi przeprowadzić dowolne obliczenie możliwe na innych znanych nam maszynach. | ||
* zużycie zasobów podczas obliczenia dobrze modeluje rzeczywiste komputery. | * 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. | 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 | 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. | 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. | ||
Linia 32: | Linia 33: | ||
==Maszyna Turinga== | ==Maszyna Turinga== | ||
[[grafika:Turing1.jpeg|thumb|right||Alan Turing (1912-1954)<br>[[Biografia Turinga|Zobacz biografię]]]] | |||
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 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. | ||
Linia 52: | Linia 49: | ||
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ą: | 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| | {{definicja|1|| | ||
Maszyna Turinga <math>M</math>, to siódemka: M <nowiki>=</nowiki> <math>(Q,\Sigma,\Gamma,\#,\delta,q_0,F)</math>, gdzie: | Maszyna Turinga <math>M</math>, to siódemka: M <nowiki>=</nowiki> <math>(Q,\Sigma,\Gamma,\#,\delta,q_0,F)</math>, gdzie: | ||
Linia 63: | Linia 60: | ||
:<math>\#</math> 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ę. | :<math>\#</math> 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ę. | ||
:<math>\delta</math> to ''funkcja przejścia''. Jest to funkcja częściowa prowadząca z <math>Q \times \Gamma</math> w <math>Q \times Gamma \times \{\leftarrow,\rightarrow\}</math>. Wartość <math>\delta(A,x) = (B,y,K)</math> oznacza, że maszyna będąca w stanie <math>A</math> i czytając symbol <math>x</math> z taśmy przejdzie do stanu <math>B</math>, zapisze symbol <math>y</math> na taśmie i przesunie głowicę o jedną komórkę w lewo (<math>K = \leftarrow</math>) lub w prawo (<math>K = \rightarrow</math>). | :<math>\delta</math> to ''funkcja przejścia''. Jest to funkcja częściowa prowadząca z <math>Q \times \Gamma</math> w <math>Q \times \Gamma \times \{\leftarrow,\rightarrow\}</math>. Wartość <math>\delta(A,x) = (B,y,K)</math> oznacza, że maszyna będąca w stanie <math>A</math> i czytając symbol <math>x</math> z taśmy przejdzie do stanu <math>B</math>, zapisze symbol <math>y</math> na taśmie i przesunie głowicę o jedną komórkę w lewo (<math>K = \leftarrow</math>) lub w prawo (<math>K = \rightarrow</math>). | ||
:<math>q_0</math> to ''stan początkowy'' w jakim znajduje się maszyna przed rozpoczęciem obliczenia. | :<math>q_0</math> to ''stan początkowy'' w jakim znajduje się maszyna przed rozpoczęciem obliczenia. | ||
Linia 80: | Linia 77: | ||
* Maszyna znajdzie się w stanie akceptującym. Mówimy wtedy, że obliczenie zakończyło się akceptująco. | * Maszyna znajdzie się w stanie akceptującym. Mówimy wtedy, że obliczenie zakończyło się akceptująco. | ||
* Maszyna znajdując się w stanie <math>A</math> przeczyta z taśmy symbol <math>x</math>, a wartość funkcji częściowej <math>\delta(A,x)</math> nie będzie zdefiniowana. Mówimy wtedy, że obliczenie zakończyło się błędem. | * Maszyna znajdując się w stanie <math>A</math> przeczyta z taśmy symbol <math>x</math>, a wartość funkcji częściowej <math>\delta(A,x)</math> 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 | 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=== | ===Reprezentacja=== | ||
Linia 88: | Linia 85: | ||
Pierwszy polega na przedstawieniu tabelki funkcji <math>\delta</math>. Możemy po prostu zapisywać w kolejnych wierszach ciągi <math>A,x: B,y,K</math> oznaczające <math>\delta(A,x) = (B,y,K)</math>. 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 <math>\#</math>. 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. | Pierwszy polega na przedstawieniu tabelki funkcji <math>\delta</math>. Możemy po prostu zapisywać w kolejnych wierszach ciągi <math>A,x: B,y,K</math> oznaczające <math>\delta(A,x) = (B,y,K)</math>. 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 <math>\#</math>. 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 | 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 <math>\leftarrow</math> lub <math>\rightarrow</math>. Strzałka etykietowana <math>x,y,K</math> prowadząca od stanu <math>A</math> do stanu <math>B</math> oznacza <math>\delta(A,x) = (B,y,K)</math>. | Następnie łączymy prostokąty strzałkami etykietowanymi trzema symbolami - dwoma symbolami taśmowymi i symbolem <math>\leftarrow</math> lub <math>\rightarrow</math>. Strzałka etykietowana <math>x,y,K</math> prowadząca od stanu <math>A</math> do stanu <math>B</math> oznacza <math>\delta(A,x) = (B,y,K)</math>. | ||
{{przyklad|[Maszyna dodająca dwie liczby w systemie unarnym]|| | {{przyklad|2 [Maszyna dodająca dwie liczby w systemie unarnym]|| | ||
}} | }} | ||
[[grafika:ZO-1.1.gif|thumb|right||Diagram maszyny dodającej dwie liczby]]<br>Możemy teraz przedstawić pierwszą maszynę Turinga. Będzie ona dodawać dwie liczby zapisane w systemie ''unarnym''. Liczba naturalna <math>n</math> jest reprezentowana w systemie unarnym przez <math>n+1</math> jedynek zapisanych na taśmie obok siebie. | |||
Zakładamy, że na wejściu maszyny znajdą się dwie liczby naturalne <math>n</math> i <math>m</math> zapisane unarnie oddzielone pojedynczym zerem. Po zakończeniu działania maszyny na taśmie powinna być zapisana liczba <math>n+m</math>. W związku z tym, językiem wejściowym maszyny będzie <math>\Sigma = \{0,1\}</math>. Maszyna będzie miała '''4''' stany <math>Q = \{A,B,C,D\}</math>, stanem początkowym będzie <math>A</math>, a jedynym stanem akceptującym będzie <math>D</math>. | |||
[[File:ZO-1.2.mp4|250x250px|thumb|center| ]] | |||
==Język maszyny. Języki rekurencyjne i rekurencyjnie przeliczalne== | ==Język maszyny. Języki rekurencyjne i rekurencyjnie przeliczalne== | ||
Linia 108: | Linia 102: | ||
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. | 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| | {{definicja|3|| | ||
Językiem maszyny <math>M</math> nazwiemy <math>L(M) \subseteq {\Sigma^\ast}</math> zbiór tych słów wejściowych, dla których obliczenie maszyny <math>M</math> kończy się w stanie akceptującym. Będziemy mówić, że <math>M</math> rozpoznaje język <math>L(M)</math>. | Językiem maszyny <math>M</math> nazwiemy <math>L(M) \subseteq {\Sigma^\ast}</math> zbiór tych słów wejściowych, dla których obliczenie maszyny <math>M</math> kończy się w stanie akceptującym. Będziemy mówić, że <math>M</math> rozpoznaje język <math>L(M)</math>. | ||
}} | }} | ||
{{definicja||| | {{definicja|4|| | ||
Język <math>L \subseteq {\Sigma^\ast}</math> nazywamy ''rekurencyjnie przeliczalnym'', albo ''częściowo rozstrzygalnym'', jeżeli istnieje taka maszyna <math>M</math>, że <math>L = L(M)</math>. | Język <math>L \subseteq {\Sigma^\ast}</math> nazywamy ''rekurencyjnie przeliczalnym'', albo ''częściowo rozstrzygalnym'', jeżeli istnieje taka maszyna <math>M</math>, że <math>L = L(M)</math>. | ||
}} | }} | ||
Linia 120: | Linia 114: | ||
Żeby uchwycić własność pełnej rozstrzygalności definiujemy własność stopu dla maszyny Turinga. | Żeby uchwycić własność pełnej rozstrzygalności definiujemy własność stopu dla maszyny Turinga. | ||
{{definicja||| | {{definicja|5|| | ||
Mówimy, że <math>M</math> ma ''własność stopu'', jeżeli dla dowolnego słowa <math>w \in {\Sigma^\ast}</math> obliczenie <math>M</math> na <math>w</math> się kończy. Język <math>L \subseteq {\Sigma^\ast}</math> nazywamy ''rekurencyjnym'', lub ''rozstrzygalnym'', jeżeli istnieje maszyna Turinga <math>M</math> z własnością stopu taka, że <math>L = L(M)</math>. | Mówimy, że <math>M</math> ma ''własność stopu'', jeżeli dla dowolnego słowa <math>w \in {\Sigma^\ast}</math> obliczenie <math>M</math> na <math>w</math> się kończy. Język <math>L \subseteq {\Sigma^\ast}</math> nazywamy ''rekurencyjnym'', lub ''rozstrzygalnym'', jeżeli istnieje maszyna Turinga <math>M</math> z własnością stopu taka, że <math>L = L(M)</math>. | ||
}} | }} | ||
Linia 126: | Linia 120: | ||
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. | 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. | ||
{{przyklad|[Maszyna rozpoznająca język <math>ww^\leftarrow</math>]|| | {{przyklad|6 [Maszyna rozpoznająca język <math>ww^\leftarrow</math>]|| | ||
}} | |||
Przedstawimy teraz drugą maszynę Turinga, której zadaniem jest rozpoznanie konkretnego języka: | [[grafika:ZO-1.3.gif|thumb|right||Diagram maszyny rozpoznającej język <math>ww^\leftarrow</math>]]Przedstawimy teraz drugą maszynę Turinga, której zadaniem jest rozpoznanie konkretnego języka: | ||
<math>\{ww^\leftarrow : w \in \{0,1\}\ast\} | |||
</math> | |||
gdzie <math>w^\leftarrow</math> oznacza słowo <math>w</math> z odwróconą kolejnością liter. | |||
''Specyfikacja problemu narzuca język wejściowy <math>\Sigma = \{0,1\}</math>. Maszyna, którą zaprezentujemy będzie miała '''7''' stanów <math>Q = \{A,B,C,D,E,F,G\}</math>. Stan <math>A</math> będzie stanem początkowym, a jedynym stanem akceptującym będzie stan'' <math>G</math>. | |||
[[File:ZO-1.4.mp4|250x250|thumb|center| ]] | |||
===Ćwiczenia=== | |||
{{cwiczenie|7 [Język maszyny przykładowej|| | |||
}} | }} | ||
Udowodnij, że maszyna z przykładu rzeczywiście rozpoznaje język <math>ww^\leftarrow</math>. | Udowodnij, że maszyna z przykładu rzeczywiście rozpoznaje język <math>ww^\leftarrow</math>. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Przeprowadź dowód indukcyjny ze względu na długość słowa wejściowego. | Przeprowadź dowód indukcyjny ze względu na długość słowa wejściowego. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Zauważmy, że jedynym stanem akceptującym jest stan <math>G</math>, a jedyne przejście do stanu <math>G</math> pochodzi ze stanu <math>A</math> przy symbolu <math>\#</math>. Ponieważ stan <math>A</math> jest stanem początkowym, oznacza to, że aby maszyna zaakceptowała niepuste słowo <math>x</math> musi ona powrócić jeszcze choć raz do stanu <math>A</math>. Przyglądając się ścieżkom, które umożliwiają powrót do stanu <math>A</math> dochodzimy do wniosku, że maszyna <math>A</math> akceptuje słowa postaci: | Zauważmy, że jedynym stanem akceptującym jest stan <math>G</math>, a jedyne przejście do stanu <math>G</math> pochodzi ze stanu <math>A</math> przy symbolu <math>\#</math>. Ponieważ stan <math>A</math> jest stanem początkowym, oznacza to, że aby maszyna zaakceptowała niepuste słowo <math>x</math> musi ona powrócić jeszcze choć raz do stanu <math>A</math>. Przyglądając się ścieżkom, które umożliwiają powrót do stanu <math>A</math> dochodzimy do wniosku, że maszyna <math>A</math> akceptuje słowa postaci: | ||
<math>x = 0y0</math> lub <math>x = 1y1</math>, gdzie <math>A</math> akceptuje słowo <math>y</math>. | * <math>x</math> jest słowem pustym. | ||
* <math>x = 0y0</math> lub <math>x = 1y1</math>, gdzie <math>A</math> akceptuje słowo <math>y</math>. | |||
Przeprowadzimy teraz dowód indukcyjny ze względu na długość słowa <math>x</math>, że <math>x</math> należy do języka maszyny wtedy i tylko wtedy, gdy <math>x=ww^\leftarrow</math> dla pewnego <math>w</math>. | Przeprowadzimy teraz dowód indukcyjny ze względu na długość słowa <math>x</math>, że <math>x</math> należy do języka maszyny wtedy i tylko wtedy, gdy <math>x=ww^\leftarrow</math> dla pewnego <math>w</math>. | ||
Linia 165: | Linia 157: | ||
</div></div> | </div></div> | ||
{{cwiczenie|8 [Mnożenie liczb unarnych]|| | |||
}} | }} | ||
Skonstruuj maszynę analogiczną do maszyny dodającej, która wykona mnożenie dwóch liczb zapisanych unarnie. | Skonstruuj maszynę analogiczną do maszyny dodającej, która wykona mnożenie dwóch liczb zapisanych unarnie. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Skonstruuj maszynę odejmującą jeden od liczby zapisanej unarnie, a następnie połącz ją z maszyną dodającą. | Skonstruuj maszynę odejmującą jeden od liczby zapisanej unarnie, a następnie połącz ją z maszyną dodającą. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Przedstawimy maszynę, która po kolei zrealizuje etapy obliczenia: | Przedstawimy maszynę, która po kolei zrealizuje etapy obliczenia: | ||
Linia 185: | Linia 174: | ||
1. skróci zapis pierwszej liczby o jedną jedynkę i przejdzie na koniec wejścia: | 1. skróci zapis pierwszej liczby o jedną jedynkę i przejdzie na koniec wejścia: | ||
<center><math>\ | <center><math>\begin{align} | ||
A,1 &: B,\#,\rightarrow \\ | A,1 &: B,\#,\rightarrow \\ | ||
B,1 &: B,1,\rightarrow \\ | B,1 &: B,1,\rightarrow \\ | ||
B,0 &: B,0,\rightarrow \\ | B,0 &: B,0,\rightarrow \\ | ||
\ | \end{align}</math></center> | ||
2. dopisze reprezentację liczby <math>0</math>, czyli jedną jedynkę za wejściem i przejdzie z powrotem na początek wejścia: | 2. dopisze reprezentację liczby <math>0</math>, czyli jedną jedynkę za wejściem i przejdzie z powrotem na początek wejścia: | ||
<center><math>\ | <center><math>\begin{align} | ||
B,\# &: C,0,\rightarrow \\ | B,\# &: C,0,\rightarrow \\ | ||
C,\# &: D,1,\leftarrow \\ | C,\# &: D,1,\leftarrow \\ | ||
Linia 201: | Linia 190: | ||
D,\# &: E,\#,\rightarrow \\ | D,\# &: E,\#,\rightarrow \\ | ||
\ | \end{align}</math></center> | ||
3. dopóki pierwsza liczba wejścia nie zostanie całkiem zamazana będzie zamazywać jedną komórkę i przechodzić do drugiej liczby: | 3. dopóki pierwsza liczba wejścia nie zostanie całkiem zamazana będzie zamazywać jedną komórkę i przechodzić do drugiej liczby: | ||
<center><math>\ | <center><math>\begin{align} | ||
E,1 &: F,\#,\rightarrow \\ | E,1 &: F,\#,\rightarrow \\ | ||
F,1 &: F,1,\rightarrow \\ | F,1 &: F,1,\rightarrow \\ | ||
F,0 &: G,0,\rightarrow \\ | F,0 &: G,0,\rightarrow \\ | ||
\ | \end{align}</math></center> | ||
4. i dopisywać ją do wyniku: | 4. i dopisywać ją do wyniku: | ||
<center><math>\ | <center><math>\begin{align} | ||
G,1 &: H,*,\rightarrow \\ | G,1 &: H,*,\rightarrow \\ | ||
H,1 &: I,*,\rightarrow \\ | H,1 &: I,*,\rightarrow \\ | ||
Linia 230: | Linia 219: | ||
M,\# &: E,\#,\rightarrow \\ | M,\# &: E,\#,\rightarrow \\ | ||
\ | \end{align}</math></center> | ||
5. a jeżeli liczba jest już zamazana, to usunie też drugą liczbę i pozostawi sam wynik: | 5. a jeżeli liczba jest już zamazana, to usunie też drugą liczbę i pozostawi sam wynik: | ||
<center><math>\ | <center><math>\begin{align} | ||
E,0 &: N,\#,\rightarrow \\ | E,0 &: N,\#,\rightarrow \\ | ||
N,1 &: N,\#,\rightarrow \\ | N,1 &: N,\#,\rightarrow \\ | ||
N,0 &: \mathbf{Z},\#,\rightarrow | N,0 &: \mathbf{Z},\#,\rightarrow | ||
\ | \end{align}</math></center> | ||
</div></div> | </div></div> | ||
{{cwiczenie|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łóż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 | |||
Załóż, że maszyna pracuje nad alfabetem <math>\Sigma = \{0,1\}</math> oraz, że słowo wejściowe jest niepuste. | Załóż, że maszyna pracuje nad alfabetem <math>\Sigma = \{0,1\}</math> oraz, że słowo wejściowe jest niepuste. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Gdyby było wiadomo, że słowo znajduje się | 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. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Dodaj dodatkowy symbol do alfabetu <math>\Gamma</math>, 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! | Dodaj dodatkowy symbol do alfabetu <math>\Gamma</math>, 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! | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Przedstawimy maszynę, która używa dodatkowego symbolu <math>* \in \Gamma</math> na oznaczenie komórek już odwiedzonych przez głowicę. Maszyna rozpoczyna w stanie <math>A</math> 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ą <math>0</math> lub <math>1</math>, to usuwa wszystkie <math>*</math> z taśmy i ustawia się na początku słowa wejściowego w stanie <math>Z</math>. | Przedstawimy maszynę, która używa dodatkowego symbolu <math>* \in \Gamma</math> na oznaczenie komórek już odwiedzonych przez głowicę. Maszyna rozpoczyna w stanie <math>A</math> 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ą <math>0</math> lub <math>1</math>, to usuwa wszystkie <math>*</math> z taśmy i ustawia się na początku słowa wejściowego w stanie <math>Z</math>. | ||
<center><math>\ | <center><math>\begin{align} | ||
A,\# &: B,*,\rightarrow \\ | A,\# &: B,*,\rightarrow \\ | ||
B,* &: B,*,\rightarrow \\ | B,* &: B,*,\rightarrow \\ | ||
Linia 283: | Linia 273: | ||
G,\# &: D,\#,\leftarrow | G,\# &: D,\#,\leftarrow | ||
\ | \end{align}</math></center> | ||
</div></div> | </div></div> | ||
{{cwiczenie|10 [Obliczenie z „czystą” taśmą]|| | |||
}} | }} | ||
Pokaż, że jeżeli maszyna <math>M</math> rozpoznaje język <math>L</math>, to można ją zmodyfikować tak, żeby nowa maszyna <math>M'</math> również rozpoznawała język <math>L</math>, ale po zakończeniu jej działania taśma była w całości wypełniona symbolem pustym. | Pokaż, że jeżeli maszyna <math>M</math> rozpoznaje język <math>L</math>, to można ją zmodyfikować tak, żeby nowa maszyna <math>M'</math> również rozpoznawała język <math>L</math>, ale po zakończeniu jej działania taśma była w całości wypełniona symbolem pustym. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Zmodyfikuj alfabet <math>\Gamma</math> tak, żeby dało się stwierdzić które komórki mogły być odwiedzone podczas obliczenia. | Zmodyfikuj alfabet <math>\Gamma</math> tak, żeby dało się stwierdzić które komórki mogły być odwiedzone podczas obliczenia. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Zauważmy, że po zakończeniu obliczenia <math>M</math> musimy zamienić wszystkie wystąpienia symboli innych niż <math>\#</math>. 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ż <math>\#</math>. | Zauważmy, że po zakończeniu obliczenia <math>M</math> musimy zamienić wszystkie wystąpienia symboli innych niż <math>\#</math>. 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ż <math>\#</math>. | ||
Zmodyfikujemy teraz działanie maszyny <math>M</math> dodając nowy symbol taśmowy <math>*</math>, który będzie miał znaczenie | Zmodyfikujemy teraz działanie maszyny <math>M</math> dodając nowy symbol taśmowy <math>*</math>, który będzie miał znaczenie „to jest symbol pusty zapisany na taśmie przez maszynę <math>M</math>”. Musimy zmienić funkcję <math>\delta</math> tak, żeby czytając znak <math>*</math> zachowywała się dokładnie tak samo, jak gdyby przeczytała <math>\#</math>. Jednocześnie za każdym razem kiedy funkcja <math>\delta</math> wskazywała, że należy zapisać na taśmie symbol <math>\#</math>, teraz należy zapisać symbol <math>*</math>. | ||
Ta modyfikacja nie zmienia języka rozpoznawanego przez <math>M</math>, gdyż możemy patrzeć na wystąpienia symbolu <math>*</math> jak na wystąpienia <math>\#</math> i wtedy całe obliczenie wygląda tak samo. | Ta modyfikacja nie zmienia języka rozpoznawanego przez <math>M</math>, gdyż możemy patrzeć na wystąpienia symbolu <math>*</math> jak na wystąpienia <math>\#</math> i wtedy całe obliczenie wygląda tak samo. | ||
Musimy teraz | 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 <math>\#</math> 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. | Zauważmy, że dla nowej maszyny ciąg symboli różnych od <math>\#</math> 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 | 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”. | ||
</div></div> | </div></div> | ||
{{cwiczenie|11 [Suma i przecięcie języków rozstrzygalnych]|| | |||
}} | }} | ||
Pokaż, że jeżeli języki <math>L_1 \in {\Sigma^\ast}</math> i <math>L_2 \in {\Sigma^\ast}</math> są rozstrzygalne, to języki <math>L_1 \cap L_2</math> i <math>L_1 \cup L_2</math> są rozstrzygalne. | Pokaż, że jeżeli języki <math>L_1 \in {\Sigma^\ast}</math> i <math>L_2 \in {\Sigma^\ast}</math> są rozstrzygalne, to języki <math>L_1 \cap L_2</math> i <math>L_1 \cup L_2</math> są rozstrzygalne. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Użyj maszyn <math>M_1</math> i <math>M_2</math> z własnością stopu rozpoznających języki <math>L_1</math> i <math>L_2</math>. Stwórz alfabet nowej maszyny <math>\Gamma = \Sigma \cup \Gamma_1 \times \Gamma_2</math> z alfabetów obu maszyn. Zamień symbol wejściowy <math>a</math> na symbol <math>(a,a)</math>. Możesz teraz tak uruchomić odpowiednio zmodyfikowane maszyny <math>M_1</math> i <math>M_2</math>, aby ich obliczenia nawzajem na siebie nie | Użyj maszyn <math>M_1</math> i <math>M_2</math> z własnością stopu rozpoznających języki <math>L_1</math> i <math>L_2</math>. Stwórz alfabet nowej maszyny <math>\Gamma = \Sigma \cup \Gamma_1 \times \Gamma_2</math> z alfabetów obu maszyn. Zamień symbol wejściowy <math>a</math> na symbol <math>(a,a)</math>. Możesz teraz tak uruchomić odpowiednio zmodyfikowane maszyny <math>M_1</math> i <math>M_2</math>, aby ich obliczenia nawzajem na siebie nie „zachodziły”. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Postępujemy zgodnie z podpowiedzią i używając maszyn z własnością stopu <math>M_1</math> i <math>M_2</math> rozpoznających odpowiednio <math>L_1</math> i <math>L_2</math> definiujemy nową maszynę <math>M</math>, która ma zasymulować uruchomienie obu maszyn na tym samym wejściu. | Postępujemy zgodnie z podpowiedzią i używając maszyn z własnością stopu <math>M_1</math> i <math>M_2</math> rozpoznających odpowiednio <math>L_1</math> i <math>L_2</math> definiujemy nową maszynę <math>M</math>, która ma zasymulować uruchomienie obu maszyn na tym samym wejściu. | ||
Linia 324: | Linia 315: | ||
Alfabetem symboli taśmowych będzie <math>\Gamma = \Sigma \cup \Gamma_1 \times \Gamma_2</math>. Symbolem pustym jest teraz <math>(\#_1,\#_2)</math>. | Alfabetem symboli taśmowych będzie <math>\Gamma = \Sigma \cup \Gamma_1 \times \Gamma_2</math>. Symbolem pustym jest teraz <math>(\#_1,\#_2)</math>. | ||
Dokonujemy modyfikacji maszyn <math>M_1</math> i <math>M_2</math>, tak żeby po zakończeniu ich działania taśma była pusta oprócz jednej komórki zawierającej symbol | Dokonujemy modyfikacji maszyn <math>M_1</math> i <math>M_2</math>, 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 <math>M_1</math> w swoim obliczeniu korzystała tylko z pierwszej współrzędnej alfabetu taśmowego. Po prostu produkcję <math>\delta_1(A,x) = (B,y,K)</math> zamieniamy na zbiór produkcji postaci <math>\delta(A,(x,g)) = (B,(y,g),K)</math> po wszystkich <math>g \in \Gamma_2</math>. Podobną modyfikację wykonujemy dla drugiej maszyny. | Następnie wprowadzamy dalsze modyfikacje tak, żeby maszyna <math>M_1</math> w swoim obliczeniu korzystała tylko z pierwszej współrzędnej alfabetu taśmowego. Po prostu produkcję <math>\delta_1(A,x) = (B,y,K)</math> zamieniamy na zbiór produkcji postaci <math>\delta(A,(x,g)) = (B,(y,g),K)</math> po wszystkich <math>g \in \Gamma_2</math>. Podobną modyfikację wykonujemy dla drugiej maszyny. | ||
Wiemy, że maszyny <math>M_1</math> i <math>M_2</math> miały własność stopu. Ich modyfikacje nie zmieniły tego faktu. Możemy w związku z tym skonstruować maszynę <math>M</math> z własnością stopu, która po kolei: | Wiemy, że maszyny <math>M_1</math> i <math>M_2</math> miały własność stopu. Ich modyfikacje nie zmieniły tego faktu. Możemy w związku z tym skonstruować maszynę <math>M</math> z własnością stopu, która po kolei: | ||
* przepisuje litery słowa wejściowego <math>a \in \Sigma</math> na pary <math>(a,a)</math>. | |||
* wraca na początek słowa i uruchamia zmodyfikowaną wersję maszyny <math>M_1</math>. | |||
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 <math>M</math> 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 | * odszukuje początek słowa na drugich współrzędnych i uruchamia zmodyfikowaną wersję maszyny <math>M_2</math>. | ||
* 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 <math>M</math> 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 <math>M_1</math> może się nigdy nie zakończyć dla słów należących do <math>M_2</math>. Aby rozwiązać takie zadanie będziemy musieli użyć innych technik. | 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 <math>M_1</math> może się nigdy nie zakończyć dla słów należących do <math>M_2</math>. Aby rozwiązać takie zadanie będziemy musieli użyć innych technik. | ||
</div></div> | </div></div> | ||
==Zapis programu i stanu maszyny== | ==Zapis programu i stanu maszyny== | ||
Linia 347: | Linia 336: | ||
Takiego kodowania maszyny na taśmie można dokonać na wiele różnych sposobów. Przyjrzymy się jednemu z nich. | 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 | Zauważmy, że dla działania maszyny nie są istotne „nazwy” elementów ze zbioru <math>\Gamma</math> i <math>Q</math>. Możemy zatem założyć, że są to kolejne liczby naturalne. Wtedy do opisu maszyny wystarczy zapisać: | ||
<math>q</math> - liczbę elementów w zbiorze <math>Q</math>. Dalej zakładamy, że <math>Q = \{0,1,\ldots, | 1. <math>q</math> - liczbę elementów w zbiorze <math>Q</math>. Dalej zakładamy, że <math>Q = \{0,1,\ldots,q-1\}</math>. | ||
<math>s</math> - liczbę elementów w zbiorze <math>\Sigma</math>. | 2. <math>s</math> - liczbę elementów w zbiorze <math>\Sigma</math>. | ||
<math>g</math> - liczbę elementów w zbiorze <math>\Gamma</math>. Wygodnie jest założyć, że numeracja w <math>\Sigma \subseteq \Gamma</math> pokrywa się z tą w <math>\Gamma</math>. Dzięki temu nie trzeba rozróżniać tych dwóch numeracji. | 3. <math>g</math> - liczbę elementów w zbiorze <math>\Gamma</math>. Wygodnie jest założyć, że numeracja w <math>\Sigma \subseteq \Gamma</math> pokrywa się z tą w <math>\Gamma</math>. Dzięki temu nie trzeba rozróżniać tych dwóch numeracji. | ||
<math>s \le b < g</math> - numer symbolu pustego. | 4. <math>s \le b < g</math> - numer symbolu pustego. | ||
<math>d</math> - liczbę zdefiniowanych wartości funkcji <math>\delta</math>. | 5. <math>d</math> - liczbę zdefiniowanych wartości funkcji <math>\delta</math>. | ||
Następnie <math>d</math> piątek liczb naturalnych <math>A < q</math>, <math>X < g</math>, <math>B < q</math>, <math>Y < g</math>, <math>K \in \{0,1\}</math> oznaczających, że | 6. Następnie <math>d</math> piątek liczb naturalnych <math>A < q</math>, <math>X < g</math>, <math>B < q</math>, <math>Y < g</math>, <math>K \in \{0,1\}</math> oznaczających, że <br/> | ||
<math>\delta(A,X) = (B,Y,\leftarrow)</math>, gdy <math>K=0</math>. <br/> | |||
<math>\delta(A,X) = (B,Y,\leftarrow)</math>, gdy <math>K=0</math>. | <math>\delta(A,X) = (B,Y,\rightarrow)</math>, gdy <math>K=1</math>. | ||
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 <math>\{0,1\}</math>. Bardzo istotne jest, że słowo to ma skończoną długość - będziemy z tej skończonej reprezentacji nieraz korzystać. | 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 <math>\{0,1\}</math>. Bardzo istotne jest, że słowo to ma skończoną długość - będziemy z tej skończonej reprezentacji nieraz korzystać. | ||
===Ćwiczenia=== | ===Ćwiczenia=== | ||
{{cwiczenie|| | {{cwiczenie|12 [Konfiguracja maszyny]|| | ||
}} | |||
Podobnie jak zapisaliśmy maszynę jako słowo wejściowe nad alfabetem <math>\{0,1\}</math>, możemy w podobny sposób zakodować opis sytuacji w jakiej znajduje się obliczenie maszyny w dowolnej chwili. | Podobnie jak zapisaliśmy maszynę jako słowo wejściowe nad alfabetem <math>\{0,1\}</math>, możemy w podobny sposób zakodować opis sytuacji w jakiej znajduje się obliczenie maszyny w dowolnej chwili. | ||
Linia 375: | Linia 363: | ||
Przedstaw takie kodowanie tego opisu. | Przedstaw takie kodowanie tego opisu. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Dla zakodowania całego opisu obliczenia maszyny trzeba zapisać stan maszyny, zawartość taśmy i pozycję głowicy. | Dla zakodowania całego opisu obliczenia maszyny trzeba zapisać stan maszyny, zawartość taśmy i pozycję głowicy. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Zauważmy, że zawartość taśmy, mimo że jest potencjalnie nieskończona, to w każdym momencie obliczenia wymagane jest opisanie tylko skończonej liczby komórek - wiemy, że pozostałe są wypełnione symbolem pustym. Możemy zatem zapisać ciąg liczb w systemie unarnym pooddzielanych zerami: | Zauważmy, że zawartość taśmy, mimo że jest potencjalnie nieskończona, to w każdym momencie obliczenia wymagane jest opisanie tylko skończonej liczby komórek - wiemy, że pozostałe są wypełnione symbolem pustym. Możemy zatem zapisać ciąg liczb w systemie unarnym pooddzielanych zerami: | ||
Następnie ciąg <math>T</math> składający się z <math>t</math> liczb, gdzie liczba <math>T_i</math> oznacza numer symbolu z <math>\Gamma</math>, który jest zapisany w <math>i</math>-tej komórce. | * <math>q</math> - numer stanu w którym znajduje się maszyna | ||
* <math>t</math> - liczba komórek taśmy, które wymagają opisu | |||
* Następnie ciąg <math>T</math> składający się z <math>t</math> liczb, gdzie liczba <math>T_i</math> oznacza numer symbolu z <math>\Gamma</math>, który jest zapisany w <math>i</math>-tej komórce. | |||
<math>h</math> - numer komórki w ciągu <math>T</math> nad którą znajduje się głowica. | * <math>h</math> - numer komórki w ciągu <math>T</math> nad którą znajduje się głowica. | ||
</div></div> | </div></div> | ||
==Zasoby potrzebne do obliczenia maszyny== | ==Zasoby potrzebne do obliczenia maszyny== | ||
Linia 395: | Linia 383: | ||
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. | 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| | {{definicja|13|| | ||
Czas obliczenia maszyny <math>M</math> na konkretnym słowie wejściowym <math>w</math> - <math>T(M,w)</math> to liczba kroków wykonanych zanim maszyna się zatrzymała. Jeżeli <math>M</math> nigdy się nie zatrzyma, to mówimy, że czas obliczenia jest nieskończony i zapisujemy <math>T(M,w) = \infty</math>. | Czas obliczenia maszyny <math>M</math> na konkretnym słowie wejściowym <math>w</math> - <math>T(M,w)</math> to liczba kroków wykonanych zanim maszyna się zatrzymała. Jeżeli <math>M</math> nigdy się nie zatrzyma, to mówimy, że czas obliczenia jest nieskończony i zapisujemy <math>T(M,w) = \infty</math>. | ||
}} | }} | ||
Linia 401: | Linia 389: | ||
Możemy też zdefiniować czas działania samej maszyny. | Możemy też zdefiniować czas działania samej maszyny. | ||
{{definicja| | {{definicja|14|| | ||
Funkcja <math>f : \mathbb{N} \rightarrow \mathbb{N}</math> jest ''złożonością czasową'' dla maszyny <math>M</math>, jeżeli | Funkcja <math>f : \mathbb{N} \rightarrow \mathbb{N}</math> jest ''złożonością czasową'' dla maszyny <math>M</math>, jeżeli | ||
<center><math> | |||
{n | \forall_{n \in \mathbb{N}}: f(n) = max \{ T(M,w) : w \in \Sigma^n \} | ||
</math></center> | |||
}} | }} | ||
Linia 412: | Linia 400: | ||
Wprowadzamy również podobne definicje dla pamięci wymaganej dla obliczenia związane z liczbą komórek taśmy użytych podczas obliczenia. | Wprowadzamy również podobne definicje dla pamięci wymaganej dla obliczenia związane z liczbą komórek taśmy użytych podczas obliczenia. | ||
{{definicja| | {{definicja|15|| | ||
Pamięć potrzebną do obliczenia maszyny <math>M</math> na słowie <math>w</math> - <math>S(M,w)</math> definiujemy jako liczbę komórek taśmy, które zostały odwiedzone przez głowicę zanim maszyna się zatrzymała. Jeżeli <math>M</math> nie zatrzymuje się na <math>w</math>, to wartość <math>S(M,w)</math> jest nieokreślona. | Pamięć potrzebną do obliczenia maszyny <math>M</math> na słowie <math>w</math> - <math>S(M,w)</math> definiujemy jako liczbę komórek taśmy, które zostały odwiedzone przez głowicę zanim maszyna się zatrzymała. Jeżeli <math>M</math> nie zatrzymuje się na <math>w</math>, to wartość <math>S(M,w)</math> jest nieokreślona. | ||
}} | }} | ||
{{definicja| | {{definicja|16|| | ||
Mówimy, że funkcja <math>f : \mathbb{N} \rightarrow \mathbb{N}</math> jest ''złożonością pamięciową'' maszyny <math>M</math> z własnością stopu, jeżeli | Mówimy, że funkcja <math>f : \mathbb{N} \rightarrow \mathbb{N}</math> jest ''złożonością pamięciową'' maszyny <math>M</math> z własnością stopu, jeżeli | ||
<center><math> | |||
{n | \forall_{n \in \mathbb{N}}: f(n) = max \{ S(M,w) : w \in \Sigma^n \} | ||
</math></center> | |||
Dzięki założeniu o własności stopu dla maszyny <math>M</math> wszystkie wartości są dobrze określone, a <math>f(n)</math> jest równe pamięci wystarczającej do wykonania obliczenia <math>M</math> na dowolnym <math>n</math>-literowym słowie wejściowym. | |||
}} | }} | ||
===Ćwiczenia=== | ===Ćwiczenia=== | ||
{{cwiczenie| | {{cwiczenie|17 [Czas i pamięć potrzebna przykładowym maszynom]|| | ||
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 <math>ww^\leftarrow</math>. | Spróbuj określić złożoność czasową i pamięciową dla przykładowych maszyn - dodającej i rozpoznającej język <math>ww^\leftarrow</math>. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
{| border="1" cellspacing="0" | |||
! !! Złożoność czasowa !! Złożoność pamięciowa | |||
|- | |||
! Maszyna dodająca || <math>f(0) = 1</math><br/><math>f(1) = 3</math><br/><math>f(n) = n+3; n\geq2</math> || <math>f(0) = 2</math><br/><math>f(1) = 3</math><br/><math>f(n) = n+1; n\geq2</math> | |||
|- | |||
! Maszyna rozpoznająca <math>ww^\leftarrow</math> || <math>f(n) = 6 + 8 + \ldots + (n+3) + 2 ; n=2k+1</math><br/><math>f(n) = 5 + 7 + \ldots + (n+3) + 1 ; n=2k</math> || <math>f(n) = n+1</math> | |||
|} | |||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Żeby określić złożoność czasową i pamięciową maszyny dodającej wystarczy zauważyć, że maszyna czytając dowolny ciąg <math>n</math> zer i jedynek przesunie się <math>n</math> razy w prawo (dochodząc do <math>n+1</math> pozycji na której jest zero), a potem cofnie się co najwyżej trzy razy (o ile są dwie jedynki na końcu). Ponieważ dla słów krótszych niż <math>2</math>-znakowe niemożliwe są dwie jedynki na końcu, a możliwe jest wycofanie głowicy przed słowo wejściowe - należy te przypadki rozważyć osobno. Otrzymuje się wyniki podane w podpowiedzi. | Żeby określić złożoność czasową i pamięciową maszyny dodającej wystarczy zauważyć, że maszyna czytając dowolny ciąg <math>n</math> zer i jedynek przesunie się <math>n</math> razy w prawo (dochodząc do <math>n+1</math> pozycji na której jest zero), a potem cofnie się co najwyżej trzy razy (o ile są dwie jedynki na końcu). Ponieważ dla słów krótszych niż <math>2</math>-znakowe niemożliwe są dwie jedynki na końcu, a możliwe jest wycofanie głowicy przed słowo wejściowe - należy te przypadki rozważyć osobno. Otrzymuje się wyniki podane w podpowiedzi. | ||
Linia 464: | Linia 433: | ||
Złożoność czasowa wynika w rekurencyjnego wzoru <math>f(n) = n+3 + f(n-2)</math> z bazą <math>f(0) = 1</math> i <math>f(1) = 2</math>. Rozwiązaniem rekursji dla <math>n\geq2</math> jest: | Złożoność czasowa wynika w rekurencyjnego wzoru <math>f(n) = n+3 + f(n-2)</math> z bazą <math>f(0) = 1</math> i <math>f(1) = 2</math>. Rozwiązaniem rekursji dla <math>n\geq2</math> jest: | ||
<center><math>\ | <center><math>\begin{align} | ||
f(n) = \frac{n-1}{2}(6+\frac{n+3}{2}); n=2k+1\\ | f(n) = \frac{n-1}{2}(6+\frac{n+3}{2}); n=2k+1\\ | ||
f(n) = \frac{n}{2}(5+\frac{n+3}{2}); n=2k | f(n) = \frac{n}{2}(5+\frac{n+3}{2}); n=2k | ||
\ | \end{align}</math></center> | ||
Jak widać, nawet dla prostych maszyn oszacowanie dokładnych wartości złożoności czasowej i pamięciowej przysparza sporych kłopotów. Dlatego prawie nigdy nie będziemy rozważać dokładnych wartości tych funkcji i będziemy szeroko korzystać z notacji <math>\mathcal{O}</math>. | Jak widać, nawet dla prostych maszyn oszacowanie dokładnych wartości złożoności czasowej i pamięciowej przysparza sporych kłopotów. Dlatego prawie nigdy nie będziemy rozważać dokładnych wartości tych funkcji i będziemy szeroko korzystać z notacji <math>\mathcal{O}</math>. | ||
</div></div> | </div></div> | ||
==Wielotaśmowa maszyna Turinga== | ==Wielotaśmowa maszyna Turinga== | ||
Interesującym rozszerzeniem modelu maszyny Turinga jest dodanie do jej mechanizmu dodatkowych taśm. <math>k</math>-taśmowa maszyna Turinga różni się od zwykłej maszyny Turinga tym, że ma <math>k</math> 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. | [[grafika:ZO-1.5.gif|thumb|right||Wielotaśmowa maszyna Turinga]]<br>Interesującym rozszerzeniem modelu maszyny Turinga jest dodanie do jej mechanizmu dodatkowych taśm. <math>k</math>-taśmowa maszyna Turinga różni się od zwykłej maszyny Turinga tym, że ma <math>k</math> 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. | Ż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=== | ||
Linia 500: | Linia 460: | ||
===Ćwiczenia=== | ===Ćwiczenia=== | ||
{{cwiczenie| | {{cwiczenie|18 [Definicje funkcji przejścia]|| | ||
Definicje funkcji przejścia | }} | ||
Sformalizuj definicję funkcji przejścia dla <math>k</math>-taśmowej maszyny Turinga w wersji normalnej i off-line. | Sformalizuj definicję funkcji przejścia dla <math>k</math>-taśmowej maszyny Turinga w wersji normalnej i off-line. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Przyjrzyj się definicji zwykłej maszyny Turinga i zmodyfikuj jej argumenty i wynik tak, żeby można było wyrazić dostęp do <math>k</math> taśm. Który element jest niepotrzebny w wypadku maszyny off-line? | Przyjrzyj się definicji zwykłej maszyny Turinga i zmodyfikuj jej argumenty i wynik tak, żeby można było wyrazić dostęp do <math>k</math> taśm. Który element jest niepotrzebny w wypadku maszyny off-line? | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Formalna definicja funkcji przejścia dla <math>k</math>-taśmowej maszyny Turinga musi zależeć od stanu i odczytu ze wszystkich <math>k</math> taśm i decydować o zmianie stanu i wszystkich <math>k</math> głowic. W związku z tym ma postać: | Formalna definicja funkcji przejścia dla <math>k</math>-taśmowej maszyny Turinga musi zależeć od stanu i odczytu ze wszystkich <math>k</math> taśm i decydować o zmianie stanu i wszystkich <math>k</math> głowic. W związku z tym ma postać: | ||
: Q | <center><math>\delta : Q \times \Gamma^k \rightarrow Q \times (\Gamma \times \{\leftarrow, \bullet, \rightarrow\})^k</math></center> | ||
Wersja off-line nie może dokonywać zapisu na pierwszej taśmie, stąd funkcja przyjmuje postać: | Wersja off-line nie może dokonywać zapisu na pierwszej taśmie, stąd funkcja przyjmuje postać: | ||
: Q | <center><math>\delta : Q \times \Gamma^k \rightarrow Q \times \{\leftarrow, \bullet, \rightarrow\} \times (\Gamma \times \{\leftarrow, \bullet, \rightarrow\})^{k-1}</math></center> | ||
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ć: | 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ć: | ||
: Q | <center><math>\delta : Q \times \Gamma^{k-1} \rightarrow Q \times \{\leftarrow, \bullet, \rightarrow\} \times (\Gamma \times \{\leftarrow, \bullet, \rightarrow\})^{k-2} \times \Gamma \times \{\bullet, \rightarrow\} | ||
</math></center> | |||
</div></div> | </div></div> | ||
{{cwiczenie|19 [Symulacja maszyny <math>k</math>-taśmowej]|| | |||
}} | |||
Pokaż, że dla języka rozpoznawanego przez <math>k</math>-taśmową maszynę Turinga <math>M_{(k)}</math> o złożoności czasowej <math>\mathcal{O}(f(n))</math> da się skonstruować zwykłą maszynę Turinga <math>M</math> o złożoności czasowej <math>\mathcal{O}(f(n)^2)</math>. | Pokaż, że dla języka rozpoznawanego przez <math>k</math>-taśmową maszynę Turinga <math>M_{(k)}</math> o złożoności czasowej <math>\mathcal{O}(f(n))</math> da się skonstruować zwykłą maszynę Turinga <math>M</math> o złożoności czasowej <math>\mathcal{O}(f(n)^2)</math>. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Wykorzystaj pomysł z ćwiczenia o sumie i przecięciu języków rekurencyjnych, aby zakodować zawartość <math>k</math>-taśm na jednej taśmie. Użyj kopii alfabetu taśmowego do zaznaczenia pozycji głowicy na każdej z taśm. | Wykorzystaj pomysł z ćwiczenia o sumie i przecięciu języków rekurencyjnych, aby zakodować zawartość <math>k</math>-taśm na jednej taśmie. Użyj kopii alfabetu taśmowego do zaznaczenia pozycji głowicy na każdej z taśm. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Zakładamy, że maszyna <math>M_{(k)}</math> została zmodyfikowana tak jak w ćwiczeniu o | Zakładamy, że maszyna <math>M_{(k)}</math> została zmodyfikowana tak jak w ćwiczeniu o „czystym” obliczeniu i używa symbolu <math>*</math> do oznaczenia komórek z symbolem pustym zapisanym podczas obliczenia. Pozwoli to określić które fragmenty taśmy zawierają „istotne” informacje. | ||
Maszyna <math>M</math> będzie pracować przy alfabecie wejściowym <math>\Sigma = \Sigma_{(k)}</math> i taśmowym <math>\Gamma = \Sigma_{k} \cup {(\Gamma_{(k)} \cup \underline{\Gamma_{(k)}})}^k</math>. Gdzie <math>\underline{\Gamma_{(k)}} = \{ \underline{A} : A \in \Gamma_{(k)}\}</math> jest kopią alfabetu taśmowego, a symbol <math>\underline{A}</math> oznacza, że na taśmie jest symbol <math>A</math> i głowica znajduje się nad tą właśnie komórką. | Maszyna <math>M</math> będzie pracować przy alfabecie wejściowym <math>\Sigma = \Sigma_{(k)}</math> i taśmowym <math>\Gamma = \Sigma_{k} \cup {(\Gamma_{(k)} \cup \underline{\Gamma_{(k)}})}^k</math>. Gdzie <math>\underline{\Gamma_{(k)}} = \{ \underline{A} : A \in \Gamma_{(k)}\}</math> jest kopią alfabetu taśmowego, a symbol <math>\underline{A}</math> oznacza, że na taśmie jest symbol <math>A</math> i głowica znajduje się nad tą właśnie komórką. | ||
Linia 539: | Linia 498: | ||
Maszyna <math>M</math> będzie działać w następujący sposób: | Maszyna <math>M</math> będzie działać w następujący sposób: | ||
* przepisze wejście z wartości <math>a</math> na <math>(\underline{a},\underline{\#},\ldots,\underline{\#})</math> w wypadku pierwszego symbolu i <math>(a,\#,\#,\ldots,\#)</math> w wypadku pozostałych. | |||
* będąc w stanie odpowiadającym stanowi <math>q</math> maszyny <math>M_{(k)}</math> i na lewym końcu taśmy (do znalezienia lewego końca taśmy używamy symboli <math>*</math> i metody z ćwiczenia o ,,czystym'' obliczeniu) | |||
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 <math>M_{(k)}</math>. | * przejdzie do stanu <math>(q,\sqcup,\sqcup,\ldots,\sqcup)</math> 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 <math>(x_1,\ldots,\underline{x_i},\ldots,x_k)</math>, to w stanie <math>(q,A_1,\ldots,A_i,\ldots,A_k)</math> wartość na <math>i</math>-tej współrzędnej stanu zmienia się z <math>\sqcup</math> na <math>x_i</math>. | ||
* 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 <math>M_{(k)}</math>. | |||
Jeżeli maszyna <math>M_{(k)}</math> działa dla wejścia rozmiaru <math>n</math> w czasie <math>\mathcal{O}(f(n))</math>, to działanie maszyny <math>M</math> zakończy się po wykonaniu <math>\mathcal{O}(f(n))</math> 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 <math>\mathcal{O}(f(n))</math> komórek na lewo i <math>\mathcal{O}(f(n))</math> komórek na prawo od pozycji początkowej - gdyż żadna z głowic w czasie <math>\mathcal{O}(f(n))</math> nie może pokonać większej odległości. W związku z tym otrzymujemy oszacowanie <math>\mathcal{O}(f(n)^2)</math> na złożoność czasową wynikowej maszyny. | Jeżeli maszyna <math>M_{(k)}</math> działa dla wejścia rozmiaru <math>n</math> w czasie <math>\mathcal{O}(f(n))</math>, to działanie maszyny <math>M</math> zakończy się po wykonaniu <math>\mathcal{O}(f(n))</math> 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 <math>\mathcal{O}(f(n))</math> komórek na lewo i <math>\mathcal{O}(f(n))</math> komórek na prawo od pozycji początkowej - gdyż żadna z głowic w czasie <math>\mathcal{O}(f(n))</math> nie może pokonać większej odległości. W związku z tym otrzymujemy oszacowanie <math>\mathcal{O}(f(n)^2)</math> na złożoność czasową wynikowej maszyny. | ||
</div></div> | </div></div> | ||
{{cwiczenie|20 [Symulacja na maszynie off-line]|| | |||
}} | |||
Pokaż jak zasymulować <math>k</math>-taśmową maszynę Turinga na <math>2</math>-taśmowej maszynie off-line. | Pokaż jak zasymulować <math>k</math>-taśmową maszynę Turinga na <math>2</math>-taśmowej maszynie off-line. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Użyj rezultatu z poprzedniego ćwiczenia. | Użyj rezultatu z poprzedniego ćwiczenia. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Używając konstrukcji pozwalającej zasymulować <math>k</math>-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. | Używając konstrukcji pozwalającej zasymulować <math>k</math>-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. | ||
</div></div> | </div></div> | ||
==Niedeterministyczna maszyna Turinga== | ==Niedeterministyczna maszyna Turinga== | ||
Linia 569: | Linia 525: | ||
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. | 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| | {{definicja|21|| | ||
Niedeterministyczna maszyna Turinga, to maszyna Turinga, w której funkcja przejścia jest częściową funkcją wielowartościową: | Niedeterministyczna maszyna Turinga, to maszyna Turinga, w której funkcja przejścia jest częściową funkcją wielowartościową: | ||
: Q | <center><math>\delta : Q \times \Gamma \rightarrow \mathcal{P}(Q \times \Gamma \times \{\leftarrow,\rightarrow\})</math></center> | ||
}} | }} | ||
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. | 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| | {{definicja|22|| | ||
Mówimy, że niedeterministyczna maszyna <math>M</math> akceptuje słowo <math>w</math> jeżeli ''istnieje'' taki ciąg przejść, który kończy się stanem akceptującym. | Mówimy, że niedeterministyczna maszyna <math>M</math> akceptuje słowo <math>w</math> 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 | 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 <math>P\text{ vs }NP</math>, który omówimy dokładnie w dalszej części kursu. | 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 <math>P\text{ vs }NP</math>, który omówimy dokładnie w dalszej części kursu. | ||
{{przyklad|[ | {{przyklad|23 [Drzewo możliwych wykonań]|| | ||
[[grafika:ZO-1.6-rys.gif|thumb|right||Drzewo możliwych wykonań z zaznaczonymi wykonaniami akceptującymi]]<br>Przyjrzyjmy się teraz diagramowi przykładowej niedeterministycznej maszyny Turinga. Można zauważyć, że niedeterminizm pojawia się w stanie <math>A</math>. Maszyna będąc w stanie <math>A</math> i czytając z taśmy symbol <math>0</math> może zarówno: | |||
* pozostawić <math>0</math> na taśmie i przesunąć głowicę w prawo nie zmieniając stanu. | |||
* zamienić <math>0</math> na <math>1</math>, przesunąć się w prawo i zmienić stan na <math>B</math>. | |||
zamienić <math>0</math> na <math>1</math>, przesunąć się w prawo i zmienić stan na <math>B</math>. | |||
Podobnie reakcja maszyny na symbol <math>1</math> może być dwojaka. | Podobnie reakcja maszyny na symbol <math>1</math> może być dwojaka. | ||
Linia 614: | Linia 561: | ||
Przede wszystkim definiujemy, że niedeterministyczna maszyna <math>M</math> zatrzymuje się na słowie <math>w</math>, kiedy żaden z możliwych scenariuszy obliczenia nie jest nieskończony. | Przede wszystkim definiujemy, że niedeterministyczna maszyna <math>M</math> zatrzymuje się na słowie <math>w</math>, kiedy żaden z możliwych scenariuszy obliczenia nie jest nieskończony. | ||
{{definicja| | {{definicja|24|| | ||
Czas i pamięć zużytą przez maszynę <math>M</math>, która zatrzymuje się na słowie <math>w</math> definiujemy następująco: | Czas i pamięć zużytą przez maszynę <math>M</math>, która zatrzymuje się na słowie <math>w</math> definiujemy następująco: | ||
Pamięć potrzebną do obliczenia <math>NS(M,w)</math> jako maksimum z liczby komórek potrzebnych w każdym możliwym scenariuszu obliczenia. | * Czas wykonania <math>NT(M,w)</math> jako liczbę kroków najdłuższego z możliwych obliczeń. | ||
* Pamięć potrzebną do obliczenia <math>NS(M,w)</math> jako maksimum z liczby komórek potrzebnych w każdym możliwym scenariuszu obliczenia. | |||
}} | }} | ||
Linia 628: | Linia 575: | ||
===Ćwiczenia=== | ===Ćwiczenia=== | ||
{{cwiczenie| | {{cwiczenie|25 [Symulacja maszyny niedeterministycznej na maszynie deterministycznej]|| | ||
Symulacja maszyny niedeterministycznej na maszynie deterministycznej | }} | ||
Pokaż, że jeżeli język <math>L</math> jest rozpoznawany przez niedeterministyczną maszynę Turinga w czasie <math>\mathcal{O}(f(n))</math>, to istnieje deterministyczna maszyna Turinga rozpoznająca ten sam język w czasie <math>\mathcal{O}(c^{f(n)})</math> dla pewnej stałej <math>c</math>. | Pokaż, że jeżeli język <math>L</math> jest rozpoznawany przez niedeterministyczną maszynę Turinga w czasie <math>\mathcal{O}(f(n))</math>, to istnieje deterministyczna maszyna Turinga rozpoznająca ten sam język w czasie <math>\mathcal{O}(c^{f(n)})</math> dla pewnej stałej <math>c</math>. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Skonstruuj wielotaśmową deterministyczną maszynę, która będzie przeglądać wszystkie ścieżki drzewa możliwych wykonań. | Skonstruuj wielotaśmową deterministyczną maszynę, która będzie przeglądać wszystkie ścieżki drzewa możliwych wykonań. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Niech <math>d = max_{A \in Q,x \in \Gamma} {\vert \delta(A,x) \vert}</math>. Zauważmy, że <math>d</math> jest maksymalnym stopniem rozgałęzienia, jaki może wystąpić w drzewie możliwych obliczeń maszyny. | Niech <math>d = max_{A \in Q,x \in \Gamma} {\vert \delta(A,x) \vert}</math>. Zauważmy, że <math>d</math> jest maksymalnym stopniem rozgałęzienia, jaki może wystąpić w drzewie możliwych obliczeń maszyny. | ||
Linia 641: | Linia 587: | ||
Nasza maszyna będzie <math>4</math>-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 <math>m</math>: | Nasza maszyna będzie <math>4</math>-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 <math>m</math>: | ||
* usunie znaczniki z czwartej taśmy. | |||
* przeglądnie wszystkie ciągi <math>x</math> długości <math>m</math> liczb z przedziału <math>[1,d]</math> 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 <math>x</math> program niedeterministycznej maszyny. | |||
wyczyści trzecią taśmę używając techniki | * jeżeli obliczenie się nie zakończyło w ciągu pierwszych <math>m</math> 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 <math>m</math> 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. | Jeżeli którekolwiek ze zdeterminowanych obliczeń zakończy się akceptacją, to maszyna akceptuje słowo wejściowe. Jeżeli dla jakiejś liczby <math>m</math> 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. | ||
Linia 659: | Linia 605: | ||
Czas działania tak powstałej maszyny dla słowa wejściowego długości <math>n</math> możemy oszacować przez: | Czas działania tak powstałej maszyny dla słowa wejściowego długości <math>n</math> możemy oszacować przez: | ||
<center><math>\sum_{i=1}^{f(n)} \mathcal{O}(i)d^i \leq \sum_{i=1}^{f(n)} \mathcal{O}({(2d)}^i) = \mathcal{O}((2d)^{f(n)+1}) = \mathcal{O}(e^{f(n)})</math></center> | |||
dla pewnej stałej <math>e</math>. | |||
Możemy teraz użyć symulacji <math>4</math>-taśmowej maszyny Turinga działającej w czasie <math>\mathcal{O}(e^{f(n)})</math> na jednotaśmowej maszynie w czasie <math>\mathcal{O}((e^{f(n)})^2)</math>, czyli <math>\mathcal{O}(c^{f(n)})</math> dla <math>c=e^2</math>. | Możemy teraz użyć symulacji <math>4</math>-taśmowej maszyny Turinga działającej w czasie <math>\mathcal{O}(e^{f(n)})</math> na jednotaśmowej maszynie w czasie <math>\mathcal{O}((e^{f(n)})^2)</math>, czyli <math>\mathcal{O}(c^{f(n)})</math> dla <math>c=e^2</math>. | ||
</div></div> | </div></div> | ||
{{cwiczenie|26 [Symulacja maszyny niedeterministycznej <math>k</math>-taśmowej na <math>2</math>-taśmowej]|| | |||
}} | |||
Pokaż, że język rozpoznawany przez <math>k</math>-taśmową niedeterministyczną maszynę Turinga <math>M</math> w czasie <math>\mathcal{O}(f(n))</math> jest również rozpoznawany przez <math>2</math>-taśmową niedeterministyczną maszynę Turinga w czasie <math>\mathcal{O}(f(n))</math>. | Pokaż, że język rozpoznawany przez <math>k</math>-taśmową niedeterministyczną maszynę Turinga <math>M</math> w czasie <math>\mathcal{O}(f(n))</math> jest również rozpoznawany przez <math>2</math>-taśmową niedeterministyczną maszynę Turinga w czasie <math>\mathcal{O}(f(n))</math>. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź </span><div class="mw-collapsible-content" style="display:none"> | |||
Użyj niedeterminizmu do wylosowania obliczenia akceptującego na <math>k</math>-taśmowej maszynie, a następnie sprawdź, że wylosowałeś poprawną ścieżkę obliczeń. | Użyj niedeterminizmu do wylosowania obliczenia akceptującego na <math>k</math>-taśmowej maszynie, a następnie sprawdź, że wylosowałeś poprawną ścieżkę obliczeń. | ||
</div></div> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"> | ||
Niech <math>d</math> oznacza tak jak poprzednio <math>max_{A \in Q,x \in \Gamma} {\vert \delta(A,x) \vert}</math>. | Niech <math>d</math> oznacza tak jak poprzednio <math>max_{A \in Q,x \in \Gamma} {\vert \delta(A,x) \vert}</math>. | ||
Linia 694: | Linia 641: | ||
Czas działania nowej maszyny dla słowa długości <math>n</math> jest proporcjonalny do | Czas działania nowej maszyny dla słowa długości <math>n</math> jest proporcjonalny do | ||
<center><math>\sum_{i=0}^{[\log f(n)]} k2^i \leq 4kf(n) = \mathcal{O}(f(n))</math></center> | |||
</div></div> | </div></div> | ||
==Testy końcowe== | ==Testy końcowe== |
Aktualna wersja na dzień 22:17, 11 wrz 2023
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

Zobacz biografię
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]

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 zapisane unarnie oddzielone pojedynczym zerem. Po zakończeniu działania maszyny na taśmie powinna być zapisana liczba . W związku z tym, językiem wejściowym maszyny będzie . Maszyna będzie miała 4 stany , stanem początkowym będzie , a jedynym stanem akceptującym będzie .
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 nazwiemy zbiór tych słów wejściowych, dla których obliczenie maszyny kończy się w stanie akceptującym. Będziemy mówić, że rozpoznaje język .
Definicja 4
Język nazywamy rekurencyjnie przeliczalnym, albo częściowo rozstrzygalnym, jeżeli istnieje taka maszyna , że .
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
Mówimy, że ma własność stopu, jeżeli dla dowolnego słowa obliczenie na się kończy. Język nazywamy rekurencyjnym, lub rozstrzygalnym, jeżeli istnieje maszyna Turinga z własnością stopu taka, że .
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 ]

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 .
Ćwiczenie 8 [Mnożenie liczb unarnych]
Skonstruuj maszynę analogiczną do maszyny dodającej, która wykona mnożenie dwóch liczb zapisanych unarnie.
Ć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.
Ć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.
Ć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.
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.
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
Czas obliczenia maszyny na konkretnym słowie wejściowym - to liczba kroków wykonanych zanim maszyna się zatrzymała. Jeżeli nigdy się nie zatrzyma, to mówimy, że czas obliczenia jest nieskończony i zapisujemy .
Możemy też zdefiniować czas działania samej maszyny.
Definicja 14
Funkcja jest złożonością czasową dla maszyny , jeżeli
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
Pamięć potrzebną do obliczenia maszyny na słowie - definiujemy jako liczbę komórek taśmy, które zostały odwiedzone przez głowicę zanim maszyna się zatrzymała. Jeżeli nie zatrzymuje się na , to wartość jest nieokreślona.
Definicja 16
Mówimy, że funkcja jest złożonością pamięciową maszyny z własnością stopu, jeżeli
Dzięki założeniu o własności stopu dla maszyny wszystkie wartości są dobrze określone, a jest równe pamięci wystarczającej do wykonania obliczenia na dowolnym -literowym słowie wejściowym.
Ć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 .
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.
Ć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 .
Ćwiczenie 20 [Symulacja na maszynie off-line]
Pokaż jak zasymulować -taśmową maszynę Turinga na -taśmowej maszynie off-line.
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ń]

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
Czas i pamięć zużytą przez maszynę , która zatrzymuje się na słowie definiujemy następująco:
- Czas wykonania jako liczbę kroków najdłuższego z możliwych obliczeń.
- Pamięć potrzebną do obliczenia jako maksimum z liczby komórek potrzebnych w każdym możliwym scenariuszu obliczenia.
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 .
Ć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 .