Języki, automaty i obliczenia/Wykład 13: Złożoność obliczeniowa.: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
m Zastępowanie tekstu – „.↵</math>” na „</math>”
 
(Nie pokazano 11 wersji utworzonych przez 3 użytkowników)
Linia 8: Linia 8:
teoretyczne podstawy teorii rozstrzygalności oraz przeanalizujemy
teoretyczne podstawy teorii rozstrzygalności oraz przeanalizujemy
kilka problemów nierozstrzygalnych w teorii języków.
kilka problemów nierozstrzygalnych w teorii języków.
__FORCETOC__


==Klasy złożoności obliczeniowej==
==Klasy złożoności obliczeniowej==
Linia 20: Linia 22:


{{definicja|1.1||
{{definicja|1.1||
Ustalmy funkcje <math>\displaystyle t,s:\mathbb{N}\rightarrow \mathbb{N}</math>. Mówimy, że
Ustalmy funkcje <math>t,s:\mathbb{N}\rightarrow \mathbb{N}</math>. Mówimy, że
maszyna Turinga <math>\displaystyle \mathcal{MT}</math> (deterministyczna lub
maszyna Turinga <math>\mathcal{MT}</math> (deterministyczna lub
niedeterministyczna) '''akceptuje słowo <math>\displaystyle w\in \Sigma_I^*</math> w
niedeterministyczna) '''akceptuje słowo <math>w\in \Sigma_I^*</math> w
czasie <math>\displaystyle t(|w|)</math>''', jeśli istnieje ciąg <math>\displaystyle k\leqslant t(|w|)</math> konfiguracji
czasie <math>t(|w|)</math>''', jeśli istnieje ciąg <math>k\leqslant t(|w|)</math> konfiguracji
<math>\displaystyle d_1,d_2,\dots, d_k</math> takich, że <math>\displaystyle d_1=\sharp s_0 w \sharp</math>, <math>\displaystyle d_k=
<math>d_1,d_2,\dots, d_k</math> takich, że <math>d_1=\sharp s_0 w \sharp</math>, <math>d_k=
\sharp w_{1}s_{F}w_{2}\sharp</math> dla pewnych <math>\displaystyle w_{1},w_{2}\in \Sigma
\sharp w_{1}s_{F}w_{2}\sharp</math> dla pewnych <math>w_{1},w_{2}\in \Sigma
_{T}^{*},s_{F}\in S_{F}</math> oraz <math>\displaystyle d_i \mapsto d_{i+1}</math> dla
_{T}^{*},s_{F}\in S_{F}</math> oraz <math>d_i \mapsto d_{i+1}</math> dla
<math>\displaystyle i=1,\dots,k-1</math>.
<math>i=1,\dots,k-1</math>.


Jeśli istnieje ciąg konfiguracji <math>\displaystyle d_1 \mapsto d_2 \mapsto \dots
Jeśli istnieje ciąg konfiguracji <math>d_1 \mapsto d_2 \mapsto \dots
\mapsto d_m</math>, gdzie <math>\displaystyle d_1=\sharp s_0 w \sharp</math>, <math>\displaystyle d_m</math> jest
\mapsto d_m</math>, gdzie <math>d_1=\sharp s_0 w \sharp</math>, <math>d_m</math> jest
konfiguracją akceptującą (tzn. <math>\displaystyle d_m= \sharp w_{1}s_{F}w_{2}\sharp</math>
konfiguracją akceptującą (tzn. <math>d_m= \sharp w_{1}s_{F}w_{2}\sharp</math>
dla pewnych <math>\displaystyle w_{1},w_{2}\in \Sigma _{T}^{*},s_{F}\in S_{F}</math>) oraz
dla pewnych <math>w_{1},w_{2}\in \Sigma _{T}^{*},s_{F}\in S_{F}</math>) oraz
dodatkowo <math>\displaystyle |d_i|\leqslant s(|w|)+2</math>, to mówimy, że maszyna <math>\displaystyle \mathcal{MT}</math>
dodatkowo <math>|d_i|\leqslant s(|w|)+2</math>, to mówimy, że maszyna <math>\mathcal{MT}</math>
'''akceptuje słowo <math>\displaystyle w\in \Sigma_I^*</math> w pamięci <math>\displaystyle s(|w|)</math>'''.
'''akceptuje słowo <math>w\in \Sigma_I^*</math> w pamięci <math>s(|w|)</math>'''.


Mówimy, że '''język <math>\displaystyle L</math> jest akceptowany w czasie <math>\displaystyle t(n)</math>
Mówimy, że '''język <math>L</math> jest akceptowany w czasie <math>t(n)</math>
(pamięci <math>\displaystyle s(n)</math>)''', jeśli istnieje maszyna Turinga <math>\displaystyle \mathcal{MT}</math>, dla
(pamięci <math>s(n)</math>)''', jeśli istnieje maszyna Turinga <math>\mathcal{MT}</math>, dla
której <math>\displaystyle L(\mathcal{MT})=L</math> oraz każde słowo <math>\displaystyle w\in L</math> jest
której <math>L(\mathcal{MT})=L</math> oraz każde słowo <math>w\in L</math> jest
akceptowane w czasie <math>\displaystyle t(|w|)</math> (pamięci <math>\displaystyle s(|w|)</math> odpowiednio).
akceptowane w czasie <math>t(|w|)</math> (pamięci <math>s(|w|)</math> odpowiednio).
}}
}}


Linia 48: Linia 50:
informacje, a jedynie te, do których następował zapis. Dzięki temu
informacje, a jedynie te, do których następował zapis. Dzięki temu
zabiegowi można w sposób "rozsądny" mówić o akceptacji słowa w
zabiegowi można w sposób "rozsądny" mówić o akceptacji słowa w
pamięci <math>\displaystyle \log n</math> itp. W ujęciu prezentowanym w tym wykładzie
pamięci <math>\log n</math> itp. W ujęciu prezentowanym w tym wykładzie
zajmujemy się akceptacją w pamięci <math>\displaystyle n^k</math>, dla <math>\displaystyle k\geqslant 1</math>, zatem nie ma
zajmujemy się akceptacją w pamięci <math>n^k</math>, dla <math>k\geqslant 1</math>, zatem nie ma
potrzeby dodatkowego definiowania maszyn Turinga off-line.
potrzeby dodatkowego definiowania maszyn Turinga off-line.
}}
}}


{{definicja|1.2||
{{definicja|1.2||
Oznaczmy przez <math>\displaystyle Dtime(t(n))</math> oraz <math>\displaystyle Dspace(s(n))</math> rodzinę języków
Oznaczmy przez <math>Dtime(t(n))</math> oraz <math>Dspace(s(n))</math> rodzinę języków
akceptowanych w czasie <math>\displaystyle t(n)</math> i odpowiednio pamięci <math>\displaystyle s(n)</math> przez
akceptowanych w czasie <math>t(n)</math> i odpowiednio pamięci <math>s(n)</math> przez
deterministyczną maszynę Turinga. Dla maszyn niedeterministycznych
deterministyczną maszynę Turinga. Dla maszyn niedeterministycznych
wprowadzamy w identyczny sposób klasy <math>\displaystyle Ntime(t(n))</math> oraz
wprowadzamy w identyczny sposób klasy <math>Ntime(t(n))</math> oraz
<math>\displaystyle Nspace(s(n))</math>.
<math>Nspace(s(n))</math>.


Określamy następujące klasy złożoności (klasy języków):
Określamy następujące klasy złożoności (klasy języków):


<center><math>\displaystyle \aligned \textbd{P} \displaystyle =\bigcup_{k=0}^\infty Dtime(n^k), &\qquad\qquad&
<center><math>\begin{align} \mathbf{P}  =\bigcup_{k=0}^\infty Dtime(n^k), &\qquad\qquad&
\textbd{NP} \displaystyle =\bigcup_{k=0}^\infty Ntime(n^k),
\mathbf{NP}  =\bigcup_{k=0}^\infty Ntime(n^k),
\\
\\
\textbd{PSPACE} \displaystyle =\bigcup_{k=0}^\infty Dspace(n^k), &&
\mathbf{PSPACE}  =\bigcup_{k=0}^\infty Dspace(n^k), &&
\textbd{NSPACE} \displaystyle =\bigcup_{k=0}^\infty Nspace(n^k).
\mathbf{NSPACE}  =\bigcup_{k=0}^\infty Nspace(n^k).
\endaligned</math></center>
\end{align}</math></center>


}}
}}


Wprost z definicji otrzymujemy zależności '''P''' <math>\displaystyle  \subset </math> '''NP''' oraz '''PSPACE''' <math>\displaystyle  \subset </math> '''NPSPACE''' . W dalszej części wykładu udowodnimy kilka mniej oczywistych zależności.
Wprost z definicji otrzymujemy zależności '''P''' <math>\subset</math> '''NP''' oraz '''PSPACE''' <math>\subset</math> '''NPSPACE''' . W dalszej części wykładu udowodnimy kilka mniej oczywistych zależności.


{{przyklad|1.1||
{{przyklad|1.1||
Rozważmy język:
Rozważmy język:
<center><math>\displaystyle
<center><math>
L=\left\{1^i 2^j 3^k\: k=i\cdot j,\: i,j\geqslant 1\right\}.
L = \left\{1^i 2^j 3^k: k=i\cdot j,: i,j\geqslant 1\}\right</math></center>
</math></center>


Język <math>\displaystyle L\in </math> '''P''' . Deterministyczna maszyna Turinga
Język <math>L\in</math> '''P''' . Deterministyczna maszyna Turinga
<math>\displaystyle MT_3</math> akceptująca taki język może wyglądać następująco (zaczynamy
<math>MT_3</math> akceptująca taki język może wyglądać następująco (zaczynamy
od konfiguracji <math>\displaystyle \sharp s_0 w \sharp</math>):
od konfiguracji <math>\sharp s_0 w \sharp</math>):
# Jeśli symbol pod głowicą, to <math>\displaystyle 1</math> zamień go na <math>\displaystyle\sharp</math>. Inaczej odrzuć.
# Jeśli symbol pod głowicą, to <math>1</math> zamień go na <math>\sharp</math>. Inaczej odrzuć.
# Przejdź od lewego ogranicznika do prawego, sprawdzając, czy po <math>\displaystyle 1</math> występuje <math>\displaystyle 1</math> lub <math>\displaystyle 2</math>, po <math>\displaystyle 2</math> tylko <math>\displaystyle 2</math> lub <math>\displaystyle 3</math>, a po <math>\displaystyle 3</math> kolejny symbol <math>\displaystyle 3</math> lub ogranicznik. Jeśli ta zależność nie jest spełniona, odrzuć. Gdy osiągniesz ogranicznik wykonaj następny krok.
# Przejdź od lewego ogranicznika do prawego, sprawdzając, czy po <math>1</math> występuje <math>1</math> lub <math>2</math>, po <math>2</math> tylko <math>2</math> lub <math>3</math>, a po <math>3</math> kolejny symbol <math>3</math> lub ogranicznik. Jeśli ta zależność nie jest spełniona, odrzuć. Gdy osiągniesz ogranicznik wykonaj następny krok.
# Gdy przed ogranicznikiem nie znajduje się symbol <math>\displaystyle 3</math>, odrzuć. W przeciwnym razie zamień symbol <math>\displaystyle 3</math> na <math>\displaystyle \sharp</math>, a następnie poruszaj się w lewo, aż dotrzesz do symbolu innego niż <math>\displaystyle 3</math> i <math>\displaystyle \diamondsuit</math>.
# Gdy przed ogranicznikiem nie znajduje się symbol <math>3</math>, odrzuć. W przeciwnym razie zamień symbol <math>3</math> na <math>\sharp</math>, a następnie poruszaj się w lewo, aż dotrzesz do symbolu innego niż <math>3</math> i <math>\diamondsuit</math>.
# Jeśli symbol do którego dotarłeś to <math>\displaystyle 2</math>, zamień go na <math>\displaystyle \diamondsuit</math>. Sprawdź symbol po lewej. Jeśli to <math>\displaystyle 2</math>, poruszaj się w prawo aż do ogranicznika. Następnie przejdź do kroku 3.
# Jeśli symbol do którego dotarłeś to <math>2</math>, zamień go na <math>\diamondsuit</math>. Sprawdź symbol po lewej. Jeśli to <math>2</math>, poruszaj się w prawo aż do ogranicznika. Następnie przejdź do kroku 3.
# Jeśli dotarłeś do symbolu <math>\displaystyle 1</math>, poruszaj się w lewo aż do ogranicznika. Zamień symbol <math>\displaystyle 1</math> przy ograniczniku na <math>\displaystyle \sharp</math>, a następnie idź w prawo, zamieniając wszystkie symbole <math>\displaystyle \diamondsuit</math> na <math>\displaystyle 2</math>. Gdy dojdziesz do ogranicznika, przejdź do kroku <math>\displaystyle 3</math>.
# Jeśli dotarłeś do symbolu <math>1</math>, poruszaj się w lewo aż do ogranicznika. Zamień symbol <math>1</math> przy ograniczniku na <math>\sharp</math>, a następnie idź w prawo, zamieniając wszystkie symbole <math>\diamondsuit</math> na <math>2</math>. Gdy dojdziesz do ogranicznika, przejdź do kroku <math>3</math>.
# Jeśli dotarłeś do ogranicznika, oznacza to, że skasowano już wszystkie symbole <math>\displaystyle 1</math>. Przejdź w prawo aż do ogranicznika. Jeśli natrafisz na symbol <math>\displaystyle 3</math>, odrzuć. W przeciwnym przypadku, akceptuj.
# Jeśli dotarłeś do ogranicznika, oznacza to, że skasowano już wszystkie symbole <math>1</math>. Przejdź w prawo aż do ogranicznika. Jeśli natrafisz na symbol <math>3</math>, odrzuć. W przeciwnym przypadku, akceptuj.


Nietrudno zaobserwować, że maszyna <math>\displaystyle MT_3</math> przechodzi przez taśmę w prawo i w lewo tyle
Nietrudno zaobserwować, że maszyna <math>MT_3</math> przechodzi przez taśmę w prawo i w lewo tyle
razy, ile symboli <math>\displaystyle 3</math> zawiera taśma oraz wykonuje jeden dodatkowy
razy, ile symboli <math>3</math> zawiera taśma oraz wykonuje jeden dodatkowy
przebieg na starcie. Zatem słowa z <math>\displaystyle L</math> są akceptowane w
przebieg na starcie. Zatem słowa z <math>L</math> są akceptowane w
czasie ograniczonym wielomianowo.
czasie ograniczonym wielomianowo.
}}
}}
Linia 98: Linia 99:


Rozważmy teraz język
Rozważmy teraz język
<center><math>\displaystyle
<center><math>
L=\left\{3^k\: : \: k=i\cdot j </math>  dla pewnych <math>\displaystyle  i,j> 1\rbrace.
L = \{ 3^k:k=i\cdot j \text{ dla pewnych }i,j> 1\}</math></center>
</math></center>


Najprostszą metodą uzasadnienia, że <math>\displaystyle L\in </math> '''NP'''  jest
Najprostszą metodą uzasadnienia, że <math>L\in</math> '''NP'''  jest
konstrukcja tak zwanej wyroczni. Polega ona na następującej
konstrukcja tak zwanej wyroczni. Polega ona na następującej
dwuetapowej  procedurze:
dwuetapowej  procedurze:
Linia 109: Linia 109:


W naszym przykładzie Etap 1 wygląda następująco:
W naszym przykładzie Etap 1 wygląda następująco:
# Użyj dwóch taśm. Na pierwszej z nich znajduje się <math>\displaystyle 3^k</math>.
# Użyj dwóch taśm. Na pierwszej z nich znajduje się <math>3^k</math>.
# Idź po pierwszej taśmie, wykorzystując niedeterministyczną funkcję przejść. Napotykając <math>\displaystyle 3</math>, możesz wypisać <math>\displaystyle 1</math> na taśmie drugiej i przejść o jedną komórkę w prawo na taśmie pierwszej lub przejść do następnego kroku. Jeśli dotarłeś do prawego ogranicznika taśmy pierwszej, przejdź do kroku 3.
# Idź po pierwszej taśmie, wykorzystując niedeterministyczną funkcję przejść. Napotykając <math>3</math>, możesz wypisać <math>1</math> na taśmie drugiej i przejść o jedną komórkę w prawo na taśmie pierwszej lub przejść do następnego kroku. Jeśli dotarłeś do prawego ogranicznika taśmy pierwszej, przejdź do kroku 3.
# Powróć do początku pierwszej taśmy. Wykonaj sekwencję jak w kroku <math>\displaystyle 2</math>, z tą różnicą, że teraz na drugiej taśmie wypisuj symbole <math>\displaystyle 2</math>.
# Powróć do początku pierwszej taśmy. Wykonaj sekwencję jak w kroku <math>2</math>, z tą różnicą, że teraz na drugiej taśmie wypisuj symbole <math>2</math>.
# Jako ostatnią część tego etapu przekopiuj symbole <math>\displaystyle 3</math> z pierwszej taśmy na drugą (po symbolach <math>\displaystyle 1</math> i <math>\displaystyle 2</math>).
# Jako ostatnią część tego etapu przekopiuj symbole <math>3</math> z pierwszej taśmy na drugą (po symbolach <math>1</math> i <math>2</math>).


W konstrukcji wykorzystaliśmy dwie taśmy, ale oczywiście w
W konstrukcji wykorzystaliśmy dwie taśmy, ale oczywiście w
Linia 120: Linia 120:


Etap 2 polega na weryfikacji, czy na taśmie drugiej znajduje się
Etap 2 polega na weryfikacji, czy na taśmie drugiej znajduje się
słowo postaci <math>\displaystyle 1^i 2^j 3^k</math>, gdzie <math>\displaystyle i,j>1</math> oraz <math>\displaystyle k=i\cdot j</math>. Jeśli tak, to słowo wejściowe <math>\displaystyle 3^k</math> pochodziło z języka <math>\displaystyle L</math> i akceptujemy. Można do tego wykorzystać deterministyczną maszynę Turinga, niemal identyczną z opisaną w przykładzie poprzednim.
słowo postaci <math>1^i 2^j 3^k</math>, gdzie <math>i,j>1</math> oraz <math>k=i\cdot j</math>. Jeśli tak, to słowo wejściowe <math>3^k</math> pochodziło z języka <math>L</math> i akceptujemy. Można do tego wykorzystać deterministyczną maszynę Turinga, niemal identyczną z opisaną w przykładzie poprzednim.


Jeśli słowo wejściowe pochodzi z języka <math>\displaystyle L</math>, to jedno z obliczeń maszyny niedeterministycznej z  Etapu 1. prowadzi do konstrukcji odpowiedniego słowa na drugiej taśmie. Nie wiemy, jaka dokładnie ścieżka obliczeń ma być wykorzystana, ale dla akceptacji języka <math>\displaystyle L</math> nie ma to znaczenia.
Jeśli słowo wejściowe pochodzi z języka <math>L</math>, to jedno z obliczeń maszyny niedeterministycznej z  Etapu 1. prowadzi do konstrukcji odpowiedniego słowa na drugiej taśmie. Nie wiemy, jaka dokładnie ścieżka obliczeń ma być wykorzystana, ale dla akceptacji języka <math>L</math> nie ma to znaczenia.


Zastanów się, czy da się wykazać, że także <math>\displaystyle L\in </math> '''P'''  
Zastanów się, czy da się wykazać, że także <math>L\in</math> '''P'''  
(Ćwiczenie '''1.3''', do tego wykładu).
(Ćwiczenie '''1.3''', do tego wykładu).
}}
}}


{{definicja|1.3||
{{definicja|1.3||
Funkcja <math>\displaystyle s(n)</math> jest '''konstruowalna pamięciowo''', jeśli istnieje maszyna Turinga <math>\displaystyle \mathcal{MT}=(\Sigma _{T},S,f,s_{0},S_{F})</math>, dla
Funkcja <math>s(n)</math> jest '''konstruowalna pamięciowo''', jeśli istnieje maszyna Turinga <math>\mathcal{MT}=(\Sigma _{T},S,f,s_{0},S_{F})</math>, dla
której <math>\displaystyle d_1 \mapsto^* d_2</math>, gdzie <math>\displaystyle d_1=\sharp s_0 1^n \sharp</math>,
której <math>d_1 \mapsto^* d_2</math>, gdzie <math>d_1=\sharp s_0 1^n \sharp</math>,
<math>\displaystyle d_2=\sharp s_1 1^{s(n)} w \sharp</math> dla <math>\displaystyle s_1\in S_F</math>, <math>\displaystyle w\in
<math>d_2=\sharp s_1 1^{s(n)} w \sharp</math> dla <math>s_1\in S_F</math>, <math>w\in
(\Sigma_T\setminus \left\{1\right\})^*</math> oraz dodatkowo <math>\displaystyle d_2</math> jest
(\Sigma_T\setminus \left\{1\right\})^*</math> oraz dodatkowo <math>d_2</math> jest
konfiguracją końcową.
konfiguracją końcową.
}}
}}


Inaczej mówiąc, funkcję <math>\displaystyle s(n)</math> nazywamy konstruowalną pamięciowo, jeśli istnieje maszyna Turinga <math>\displaystyle \mathcal{MT}</math>, otrzymując na wejściu
Inaczej mówiąc, funkcję <math>s(n)</math> nazywamy konstruowalną pamięciowo, jeśli istnieje maszyna Turinga <math>\mathcal{MT}</math>, otrzymując na wejściu
słowo <math>\displaystyle w</math> długości <math>\displaystyle |w|=n</math>, zaznacza na taśmie roboczej <math>\displaystyle s(n)</math> klatek i zatrzymuje się (akceptując słowo <math>\displaystyle w</math>).
słowo <math>w</math> długości <math>|w|=n</math>, zaznacza na taśmie roboczej <math>s(n)</math> klatek i zatrzymuje się (akceptując słowo <math>w</math>).


{{przyklad|1.3||
{{przyklad|1.3||
Funkcja <math>\displaystyle s(n)=2n</math> jest konstruowalna pamięciowo. Maszyna <math>\displaystyle MT_4</math>,
Funkcja <math>s(n)=2n</math> jest konstruowalna pamięciowo. Maszyna <math>MT_4</math>,
która konstruuje <math>\displaystyle s(n)</math> działa według schematu:
która konstruuje <math>s(n)</math> działa według schematu:
# Przejdź do prawego markera. Jeśli napotkano symbol inny niż <math>\displaystyle 1</math>, to odrzuć.
# Przejdź do prawego markera. Jeśli napotkano symbol inny niż <math>1</math>, to odrzuć.
# Idź w lewo aż do pierwszego symbolu <math>\displaystyle 1</math> lub markera <math>\displaystyle \sharp</math>
# Idź w lewo aż do pierwszego symbolu <math>1</math> lub markera <math>\sharp</math>
# Jeśli napotkałeś symbol <math>\displaystyle 1</math>, zamień go na <math>\displaystyle \clubsuit</math> i przejdź do prawego markera. Dopisz do słowa symbol <math>\displaystyle \clubsuit</math> (zwiększając tym samym długość słowa na taśmie o <math>\displaystyle 1</math>). Następnie powtórz cykl od <math>\displaystyle 2</math>.
# Jeśli napotkałeś symbol <math>1</math>, zamień go na <math>\clubsuit</math> i przejdź do prawego markera. Dopisz do słowa symbol <math>\clubsuit</math> (zwiększając tym samym długość słowa na taśmie o <math>1</math>). Następnie powtórz cykl od <math>2</math>.
# Jeśli napotkałeś marker, idź w prawo, zamieniając wszystkie wystąpienia <math>\displaystyle \clubsuit</math> na <math>\displaystyle 1</math>. Następnie wracaj do lewego markera i zatrzymaj się, akceptując.
# Jeśli napotkałeś marker, idź w prawo, zamieniając wszystkie wystąpienia <math>\clubsuit</math> na <math>1</math>. Następnie wracaj do lewego markera i zatrzymaj się, akceptując.


}}
}}
Linia 151: Linia 151:
{{kotwica|tw.1.1|}}{{twierdzenie|1.1 liniowa kompresja pamięci||
{{kotwica|tw.1.1|}}{{twierdzenie|1.1 liniowa kompresja pamięci||


Niech będzie dany język <math>\displaystyle L</math> oraz maszyna Turinga
Niech będzie dany język <math>L</math> oraz maszyna Turinga
<math>\displaystyle \mathcal{TM}</math> akceptująca <math>\displaystyle L</math> w pamięci <math>\displaystyle s(n)</math>. Dla dowolnego
<math>\mathcal{TM}</math> akceptująca <math>L</math> w pamięci <math>s(n)</math>. Dla dowolnego
<math>\displaystyle \varepsilon>0</math> istnieje maszyna Turinga <math>\displaystyle \mathcal{TM}'</math> akceptująca <math>\displaystyle L</math> w
<math>\varepsilon>0</math> istnieje maszyna Turinga <math>\mathcal{TM}'</math> akceptująca <math>L</math> w
pamięci <math>\displaystyle \max\left\{n,\varepsilon s(n)\right\}</math>.
pamięci <math>\max\left\{n,\varepsilon s(n)\right\}</math>.
}}
}}


{{dowod|||
{{dowod|||
(''Szkic'')
(''Szkic'')
Ustalamy liczbę naturalną <math>\displaystyle k</math>, dla której <math>\displaystyle \varepsilon k\geqslant 2</math>. Maszynę
Ustalamy liczbę naturalną <math>k</math>, dla której <math>\varepsilon k\geqslant 2</math>. Maszynę
<math>\displaystyle \mathcal{TM}'</math> definiujemy następująco:
<math>\mathcal{TM}'</math> definiujemy następująco:
# Przekoduj słowo wejściowe, łącząc po <math>\displaystyle r</math> kolejnych symboli w jeden blok stanowiący nowy symbol na taśmie.
# Przekoduj słowo wejściowe, łącząc po <math>r</math> kolejnych symboli w jeden blok stanowiący nowy symbol na taśmie.
# Symuluj maszynę <math>\displaystyle \mathcal{MT}</math> na skompresowanej taśmie. Położenie głowicy wewnątrz bloku zakoduj w stanach maszyny <math>\displaystyle \mathcal{MT}'</math>.
# Symuluj maszynę <math>\mathcal{MT}</math> na skompresowanej taśmie. Położenie głowicy wewnątrz bloku zakoduj w stanach maszyny <math>\mathcal{MT}'</math>.


Zauważmy, że w kroku <math>\displaystyle 1</math>. maszyna <math>\displaystyle \mathcal{MT}'</math> wykorzystuje <math>\displaystyle n</math>
Zauważmy, że w kroku <math>1</math>. maszyna <math>\mathcal{MT}'</math> wykorzystuje <math>n</math>
komórek pamięci do odczytania słowa wejściowego. Kompresja taśmy
komórek pamięci do odczytania słowa wejściowego. Kompresja taśmy
zapewnia, że podczas symulowania maszyny <math>\displaystyle \mathcal{MT}</math> nie
zapewnia, że podczas symulowania maszyny <math>\mathcal{MT}</math> nie
wykorzystamy więcej niż <math>\displaystyle \lceil \frac{s(n)}{k}\rceil \leqslant \varepsilon s(n)</math>
wykorzystamy więcej niż <math>\lceil \frac{s(n)}{k}\rceil \leqslant \varepsilon s(n)</math>
komórek. Jednocześnie można założyć, że <math>\displaystyle \mathcal{MT}'</math> akceptuje
komórek. Jednocześnie można założyć, że <math>\mathcal{MT}'</math> akceptuje
słowa wejściowe z języka <math>\displaystyle L</math> o długości mniejszej niż <math>\displaystyle k</math> bez
słowa wejściowe z języka <math>L</math> o długości mniejszej niż <math>k</math> bez
symulowania <math>\displaystyle \mathcal{MT}</math>.
symulowania <math>\mathcal{MT}</math>.
}}
}}


{{twierdzenie|1.2 Savitch||
{{twierdzenie|1.2 Savitch||


Dla dowolnej funkcji <math>\displaystyle s(n)</math> konstruowalnej pamięciowo spełniającej
Dla dowolnej funkcji <math>s(n)</math> konstruowalnej pamięciowo spełniającej
warunek <math>\displaystyle s(n)\geqslant \log_2 n</math> prawdziwa jest inkluzja
warunek <math>s(n)\geqslant \log_2 n</math> prawdziwa jest inkluzja
<math>\displaystyle Nspace(s(n))\subset DSpace(s^2(n))</math>.
<math>Nspace(s(n))\subset DSpace(s^2(n))</math>.
}}
}}


{{dowod|||
{{dowod|||
Niech <math>\displaystyle \mathcal{NMT}</math> będzie niedeterministyczną maszyną Turinga
Niech <math>\mathcal{NMT}</math> będzie niedeterministyczną maszyną Turinga
akceptującą język <math>\displaystyle L=L(\mathcal{NMT})</math> w pamięci <math>\displaystyle s(n)</math>. Niech
akceptującą język <math>L=L(\mathcal{NMT})</math> w pamięci <math>s(n)</math>. Niech
<math>\displaystyle k(n)</math> oznacza liczbę konfiguracji potrzebną do zaakceptowania słowa
<math>k(n)</math> oznacza liczbę konfiguracji potrzebną do zaakceptowania słowa
o długości <math>\displaystyle n</math>. Istnieje liczba <math>\displaystyle c>1</math>, dla której <math>\displaystyle k(n)\leqslant c^{s(n)}</math>, co z kolei oznacza, że każde słowo o długości <math>\displaystyle n</math> jest akceptowane w <math>\displaystyle c^{s(n)}</math> krokach czasowych.
o długości <math>n</math>. Istnieje liczba <math>c>1</math>, dla której <math>k(n)\leqslant c^{s(n)}</math>, co z kolei oznacza, że każde słowo o długości <math>n</math> jest akceptowane w <math>c^{s(n)}</math> krokach czasowych.
}}
}}
Rozważmy algorytm:  
Rozważmy algorytm:  


{{algorytm|||
{{algorytm|||
   1  Wejście: słowo <math>\displaystyle w</math> długości <math>\displaystyle |w|=n</math>
   1  Wejście: słowo <math>w</math> długości <math>|w|=n</math>
   2  oblicz <math>\displaystyle s(n)</math>
   2  oblicz <math>s(n)</math>
   3  '''for''' każda konfiguracja akceptująca <math>\displaystyle d_A</math> dla której <math>\displaystyle |d_A|\leqslant s(n)</math>
   3  '''for''' każda konfiguracja akceptująca <math>d_A</math> dla której <math>|d_A|\leqslant s(n)</math>
   4    '''do if''' Test(<math>\displaystyle \sharp s_0 w \sharp</math>, <math>\displaystyle d_A</math>, <math>\displaystyle s(n) \log_2 c</math>) '''then''' akceptuj
   4    '''do if''' Test(<math>\sharp s_0 w \sharp</math>, <math>d_A</math>, <math>s(n) \log_2 c</math>) '''then''' akceptuj
}}
}}


gdzie procedura Test ma następującą postać:
gdzie procedura Test ma następującą postać:


{{algorytm|'''Procedure''' Test(<math>\displaystyle d</math>,<math>\displaystyle d'</math>,<math>\displaystyle i</math>)||
{{algorytm|'''Procedure''' Test(<math>d</math>,<math>d'</math>,<math>i</math>)||
   1  '''if''' <math>\displaystyle i=0</math> '''and''' [ (<math>\displaystyle d=d'</math>) '''or''' (<math>\displaystyle d\mapsto d'</math>)] '''then return true'''
   1  '''if''' <math>i=0</math> '''and''' [ (<math>d=d'</math>) '''or''' (<math>d\mapsto d'</math>)] '''then return true'''
   2    '''else for''' każda konfiguracja <math>\displaystyle d''</math> dla której <math>\displaystyle |d''|\leqslant s(n)</math>
   2    '''else for''' każda konfiguracja <math>d''</math> dla której <math>|d''|\leqslant s(n)</math>
   3      '''do if''' Test(<math>\displaystyle d</math>,<math>\displaystyle d''</math>,<math>\displaystyle i-1</math>) '''and''' Test <math>\displaystyle d''</math>,<math>\displaystyle d'</math>,<math>\displaystyle i-1</math>)
   3      '''do if''' Test(<math>d</math>,<math>d''</math>,<math>i-1</math>) '''and''' Test <math>d''</math>,<math>d'</math>,<math>i-1</math>)
   4        '''then return true''';
   4        '''then return true''';
   5  '''return false'''
   5  '''return false'''
Linia 208: Linia 208:
maszyny Turinga. Założenie dotyczące konstruowalności pamięciowej
maszyny Turinga. Założenie dotyczące konstruowalności pamięciowej
jest istotnie wykorzystywane w tej konstrukcji przy implementacji
jest istotnie wykorzystywane w tej konstrukcji przy implementacji
linii 3 algorytmu i linii 2 procedury Test. Musimy zaznaczyć <math>\displaystyle s(n)</math>
linii 3 algorytmu i linii 2 procedury Test. Musimy zaznaczyć <math>s(n)</math>
komórek taśmy, aby móc konstruować konfiguracje o długości
komórek taśmy, aby móc konstruować konfiguracje o długości
ograniczonej przez <math>\displaystyle s(n)</math> i móc następnie wykonywać na nich
ograniczonej przez <math>s(n)</math> i móc następnie wykonywać na nich
symulację maszyny <math>\displaystyle \mathcal{NMT}</math>.
symulację maszyny <math>\mathcal{NMT}</math>.


Zauważmy, że ilość konfiguracji jest ograniczona przez <math>\displaystyle s(n)</math>, a
Zauważmy, że ilość konfiguracji jest ograniczona przez <math>s(n)</math>, a
głębokość rekursji przez <math>\displaystyle \log c^{s(n)}</math>. Oznacza to, że jesteśmy w
głębokość rekursji przez <math>\log c^{s(n)}</math>. Oznacza to, że jesteśmy w
stanie skonstruować maszynę Turinga, która wymaga <math>\displaystyle c' s^2(n)</math> pamięci, gdzie <math>\displaystyle c'</math> jest pewną stałą. Na mocy Twierdzenia [[#tw.1.1|1.1]] jesteśmy w stanie określić maszynę <math>\displaystyle \mathcal{MT}</math> działającą w pamięci <math>\displaystyle s^2(n)</math>.
stanie skonstruować maszynę Turinga, która wymaga <math>c' s^2(n)</math> pamięci, gdzie <math>c'</math> jest pewną stałą. Na mocy Twierdzenia [[#tw.1.1|1.1]] jesteśmy w stanie określić maszynę <math>\mathcal{MT}</math> działającą w pamięci <math>s^2(n)</math>.


{{wniosek|1.1||
{{wniosek|1.1||
<center> '''PSPACE''' <math>\displaystyle  = </math> '''NPSPACE''' </center>
<center> '''PSPACE''' <math>=</math> '''NPSPACE''' </center>


}}
}}


{{lemat|1.1||
{{lemat|1.1||
Jeśli <math>\displaystyle g(n)\geqslant n</math>, to <math>\displaystyle Dtime(g(n))\subset Dspace(g(n))</math> oraz
Jeśli <math>g(n)\geqslant n</math>, to <math>Dtime(g(n))\subset Dspace(g(n))</math> oraz
<math>\displaystyle Ntime(g(n))\subset Nspace(g(n))</math>.
<math>Ntime(g(n))\subset Nspace(g(n))</math>.
}}
}}


{{dowod|||
{{dowod|||
Niech będzie dana maszyna deterministyczna <math>\displaystyle \mathcal{MT}</math>
Niech będzie dana maszyna deterministyczna <math>\mathcal{MT}</math>
akceptująca dany język <math>\displaystyle L</math> w czasie <math>\displaystyle g(n)</math>. Do akceptacji słowa <math>\displaystyle w</math>
akceptująca dany język <math>L</math> w czasie <math>g(n)</math>. Do akceptacji słowa <math>w</math>
o długości <math>\displaystyle n</math> maszyna wykorzystuje co najwyżej <math>\displaystyle g(n)</math> kroków
o długości <math>n</math> maszyna wykorzystuje co najwyżej <math>g(n)</math> kroków
czasowych, czyli odwiedza co najwyżej <math>\displaystyle g(n)+1</math> komórek taśmy.
czasowych, czyli odwiedza co najwyżej <math>g(n)+1</math> komórek taśmy.


Na podstawie Twierdzenia [[#tw.1.1|1.1]] istnieje maszyna Turinga
Na podstawie Twierdzenia [[#tw.1.1|1.1]] istnieje maszyna Turinga
<math>\displaystyle \mathcal{MT}'</math> wykorzystująca
<math>\mathcal{MT}'</math> wykorzystująca
<center><math>\displaystyle
<center><math>
\max \left\{n, \frac{1}{2}(g(n)+1)\right\}\leqslant g(n)
\max \left\{n, \frac{1}{2}(g(n)+1)\right\}\leqslant g(n)
</math></center>
</math></center>
Linia 244: Linia 244:


{{wniosek|1.2||
{{wniosek|1.2||
<center> '''P''' <math>\displaystyle  \subset </math> '''NP''' <math>\displaystyle  \subset
<center> '''P''' <math>\subset</math> '''NP''' <math>\subset
</math> '''PSPACE''' <math>\displaystyle  = </math> '''NPSPACE''' </center>
</math> '''PSPACE''' <math>=</math> '''NPSPACE''' </center>


}}
}}


{{uwaga|1.2||
{{uwaga|1.2||
Nie jest znany przykład wykazujący silną inkluzję '''P''' <math>\displaystyle  \varsubsetneq </math> '''NP''' ani dowód wykluczający istnienie takiego przykładu. Powszechnie uznawana
Nie jest znany przykład wykazujący silną inkluzję '''P''' <math>\varsubsetneq</math> '''NP''' ani dowód wykluczający istnienie takiego przykładu. Powszechnie uznawana
hipoteza głosi:  
hipoteza głosi:  
<center> '''P''' <math>\displaystyle  \neq </math> '''NP'''. </center>
<center> '''P''' <math>\neq</math> '''NP'''. </center>


Rozstrzygnięcie jej prawdziwości lub fałszywości stanowi jeden z
Rozstrzygnięcie jej prawdziwości lub fałszywości stanowi jeden z
najważniejszych, a zarazem najtrudniejszych problemów współczesnej
najważniejszych, a zarazem najtrudniejszych problemów współczesnej
informatyki. Jak widzieliśmy w Przykładzie [[#prz.1.2|1.2]], nawet w
informatyki. Jak widzieliśmy w Przykładzie [[#prz.1.2|1.2]], nawet w
przypadku konkretnego języka <math>\displaystyle L\in </math> '''NP''',  problem
przypadku konkretnego języka <math>L\in</math> '''NP''',  problem
uzasadnienia, że także <math>\displaystyle L\in </math> '''P''',  jest nietrywialny,
uzasadnienia, że także <math>L\in</math> '''P''',  jest nietrywialny,
gdyż wymaga zazwyczaj konstrukcji całkiem nowej maszyny Turinga niż
gdyż wymaga zazwyczaj konstrukcji całkiem nowej maszyny Turinga niż
ta do weryfikacji <math>\displaystyle L\in </math> '''NP'''  .
ta do weryfikacji <math>L\in</math> '''NP'''  .
}}
}}


Linia 267: Linia 267:
{{definicja|2.1 transformacja wielomianowa||
{{definicja|2.1 transformacja wielomianowa||


Niech <math>\displaystyle L_1,L_2</math> będą dowolnymi językami nad pewnym alfabetem
Niech <math>L_1,L_2</math> będą dowolnymi językami nad pewnym alfabetem
<math>\displaystyle \Sigma_I</math>. Mówimy, że <math>\displaystyle L_1</math> redukuje się do <math>\displaystyle L_2</math> w czasie
<math>\Sigma_I</math>. Mówimy, że <math>L_1</math> redukuje się do <math>L_2</math> w czasie
wielomianowym, co oznaczamy <math>\displaystyle L_1 \propto L_2</math>, gdy istnieje
wielomianowym, co oznaczamy <math>L_1 \propto L_2</math>, gdy istnieje
deterministyczna maszyna Turinga <math>\displaystyle \mathcal{MT}=(\Sigma
deterministyczna maszyna Turinga <math>\mathcal{MT}=(\Sigma
_{T},S,f,s_{0},S_{F})</math> taka, że dla dowolnego <math>\displaystyle w\in \Sigma_I^*</math>
_{T},S,f,s_{0},S_{F})</math> taka, że dla dowolnego <math>w\in \Sigma_I^*</math>
istnieje <math>\displaystyle w'\in \Sigma_I^*</math> i stan <math>\displaystyle s_1\in S_F</math> o własności
istnieje <math>w'\in \Sigma_I^*</math> i stan <math>s_1\in S_F</math> o własności
<center><math>\displaystyle
<center><math>
\sharp s_0 w \sharp \mapsto^* \sharp s_1 w' \sharp
\sharp s_0 w \sharp \mapsto^* \sharp s_1 w' \sharp
</math></center>
</math></center>


oraz
oraz
<center><math>\displaystyle
<center><math>
w\in L_1 \quad \Longleftrightarrow \quad w' \in L_2.
w\in L_1 \quad \Longleftrightarrow \quad w' \in L_2</math></center>
</math></center>


}}
}}


{{lemat|2.1||
{{lemat|2.1||
Załóżmy, że <math>\displaystyle L_1 \propto L_2</math>. Wtedy zachodzą implikacje:
Załóżmy, że <math>L_1 \propto L_2</math>. Wtedy zachodzą implikacje:
# <math>\displaystyle L_2 \in </math> '''P''' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<math>\displaystyle \quad \Longrightarrow \quad L_1 \in </math> '''P''',  
# <math>L_2 \in</math> '''P''' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<math>\quad \Longrightarrow \quad L_1 \in</math> '''P''',  
# <math>\displaystyle L_2 \in </math> '''NP''' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<math>\displaystyle \quad \Longrightarrow \quad L_1 \in </math> '''NP''',  
# <math>L_2 \in</math> '''NP''' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<math>\quad \Longrightarrow \quad L_1 \in</math> '''NP''',  
# <math>\displaystyle L_2 \in </math> '''PSPACE''' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<math>\displaystyle \quad \Longrightarrow \quad L_1 \in </math> '''PSPACE'''.
# <math>L_2 \in</math> '''PSPACE''' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<math>\quad \Longrightarrow \quad L_1 \in</math> '''PSPACE'''.


}}
}}


{{dowod|||
{{dowod|||
Dane słowo <math>\displaystyle w</math> transformujemy do <math>\displaystyle w'</math> w czasie wielomianowym, co gwarantuje założenie <math>\displaystyle L_1 \propto L_2</math>. Dzięki założeniu <math>\displaystyle L_2 \in </math> '''P'''  możemy rozstrzygnąć, czy <math>\displaystyle w'\in L_2</math> (tzn. jeśli akceptujemy <math>\displaystyle w'</math>, to robimy to w czasie wielomianowym). Tym sposobem (korzystając z definicji transformacji wielomianowej) akceptujemy <math>\displaystyle w</math> w czasie wielomianowym, o ile tylko <math>\displaystyle w\in L_1</math>. Dowód dla pozostałych implikacji jest identyczny.
Dane słowo <math>w</math> transformujemy do <math>w'</math> w czasie wielomianowym, co gwarantuje założenie <math>L_1 \propto L_2</math>. Dzięki założeniu <math>L_2 \in</math> '''P'''  możemy rozstrzygnąć, czy <math>w'\in L_2</math> (tzn. jeśli akceptujemy <math>w'</math>, to robimy to w czasie wielomianowym). Tym sposobem (korzystając z definicji transformacji wielomianowej) akceptujemy <math>w</math> w czasie wielomianowym, o ile tylko <math>w\in L_1</math>. Dowód dla pozostałych implikacji jest identyczny.
}}
}}


{{definicja|2.2||
{{definicja|2.2||
Niech <math>\displaystyle \mathcal{C}</math> oznacza pewną klasę języków. Język <math>\displaystyle L</math> nazywamy '''<math>\displaystyle \mathcal{C}</math>-trudnym''', jeśli spełniony jest warunek:
Niech <math>\mathcal{C}</math> oznacza pewną klasę języków. Język <math>L</math> nazywamy '''<math>\mathcal{C}</math>-trudnym''', jeśli spełniony jest warunek:
<center><math>\displaystyle
<center><math>
\forall\; L' \in \mathcal{C} \quad L'\propto L.
\forall\; L' \in \mathcal{C} \quad L'\propto L</math></center>
</math></center>


Jeżeli dodatkowo spełniony jest warunek <math>\displaystyle L\in \mathcal{C}</math>, to język <math>\displaystyle L</math> nazywamy '''<math>\displaystyle \mathcal{C}</math>-zupełnym'''.
Jeżeli dodatkowo spełniony jest warunek <math>L\in \mathcal{C}</math>, to język <math>L</math> nazywamy '''<math>\mathcal{C}</math>-zupełnym'''.
}}
}}


Intuicyjnie, fakt, że język jest <math>\displaystyle \mathcal{C}</math>-zupełny, oznacza, że jest on najbardziej skomplikowany (pod względem obliczeniowym) wśród języków z klasy <math>\displaystyle \mathcal{C}</math>, natomiast język <math>\displaystyle \mathcal{C}</math>-trudny jest bardziej skomplikowany niż każdy z klasy
Intuicyjnie, fakt, że język jest <math>\mathcal{C}</math>-zupełny, oznacza, że jest on najbardziej skomplikowany (pod względem obliczeniowym) wśród języków z klasy <math>\mathcal{C}</math>, natomiast język <math>\mathcal{C}</math>-trudny jest bardziej skomplikowany niż każdy z klasy
<math>\displaystyle \mathcal{C}</math>, choć sam nie musi do niej należeć.
<math>\mathcal{C}</math>, choć sam nie musi do niej należeć.


{{uwaga|2.1||
{{uwaga|2.1||
Linia 315: Linia 313:
{{przyklad|2.1||
{{przyklad|2.1||
Rozważmy języki:
Rozważmy języki:
<center><math>\displaystyle
<center><math>
L_1=\left\{1^i 2^j 3^k\: k=i\cdot j,\: i,j\geqslant 1\right\}\quad,\quad
L_1=\left\{1^i 2^j 3^k: k=i\cdot j,: i,j\geqslant 1\right\}\quad,\quad
L_2=\left\{1^i 2^j 4^{2k}\: k=i\cdot j,\: i,j\geqslant 1\right\}.
L_2=\left\{1^i 2^j 4^{2k}: k=i\cdot j,: i,j\geqslant 1\right\}</math></center>
</math></center>


Języki: <math>\displaystyle L_1</math> oraz <math>\displaystyle L_2</math> wyglądają na bardzo podobne, zatem wydaje się, że <math>\displaystyle L_1 \propto L_2</math> oraz <math>\displaystyle L_2 \propto L_1</math>. Uzasadnienie tego faktu jest prawie natychmiastowe.
Języki: <math>L_1</math> oraz <math>L_2</math> wyglądają na bardzo podobne, zatem wydaje się, że <math>L_1 \propto L_2</math> oraz <math>L_2 \propto L_1</math>. Uzasadnienie tego faktu jest prawie natychmiastowe.
}}
}}
Konstruujemy deterministyczną maszynę Turinga która działa w
Konstruujemy deterministyczną maszynę Turinga która działa w
następujący sposób:
następujący sposób:
# {{kotwica|prz.1|}} Jeśli słowo wejściowe jest puste, to stop.
# {{kotwica|prz.1|}} Jeśli słowo wejściowe jest puste, to stop.
# Przejdź do prawego ogranicznika. Jeśli przy ograniczniku nie występuje symbol <math>\displaystyle 4</math>, to wykonaj krok [[#prz.1|1]].
# Przejdź do prawego ogranicznika. Jeśli przy ograniczniku nie występuje symbol <math>4</math>, to wykonaj krok [[#prz.1|1]].
# Jeśli w słowie wejściowym występuje symbol <math>\displaystyle 4</math>, to sprawdź, czy słowo przetwarzane jest postaci <math>\displaystyle \sharp w 4^s \sharp</math>, gdzie <math>\displaystyle s\geqslant 1</math> oraz <math>\displaystyle w\in (\Sigma_I\setminus \left\{4\right\})^*</math> oraz czy dodatkowo <math>\displaystyle s</math> jest liczbą parzystą. Jeśli nie, to wykonaj krok [[#prz.1|1]].
# Jeśli w słowie wejściowym występuje symbol <math>4</math>, to sprawdź, czy słowo przetwarzane jest postaci <math>\sharp w 4^s \sharp</math>, gdzie <math>s\geqslant 1</math> oraz <math>w\in (\Sigma_I\setminus \left\{4\right\})^*</math> oraz czy dodatkowo <math>s</math> jest liczbą parzystą. Jeśli nie, to wykonaj krok [[#prz.1|1]].
# Zamień słowo <math>\displaystyle 4^s</math> na słowo <math>\displaystyle 3^{\frac{s}{2}}\sharp^{\frac{s}{2}}</math> i wykonaj krok [[#prz.1|1]].
# Zamień słowo <math>4^s</math> na słowo <math>3^{\frac{s}{2}}\sharp^{\frac{s}{2}}</math> i wykonaj krok [[#prz.1|1]].
# Przejdź nad pierwszy symbol po lewym ograniczniku i zatrzymaj się.
# Przejdź nad pierwszy symbol po lewym ograniczniku i zatrzymaj się.


W ten sposób zawsze przeprowadzamy konfigurację <math>\displaystyle \sharp s_0 w \sharp
W ten sposób zawsze przeprowadzamy konfigurację <math>\sharp s_0 w \sharp
</math> na konfigurację <math>\displaystyle \sharp s_1 w' \sharp</math>, przy czym <math>\displaystyle w'=1^i 2^j 3^k</math>
</math> na konfigurację <math>\sharp s_1 w' \sharp</math>, przy czym <math>w'=1^i 2^j 3^k</math>
tylko, gdy <math>\displaystyle w=1^i 2^j 4^{2k}</math>. Zatem <math>\displaystyle w\in L_2</math> wtedy i tylko wtedy, gdy <math>\displaystyle w'\in L_1</math>. Wykazaliśmy, że <math>\displaystyle L_2 \propto L_1</math>.
tylko, gdy <math>w=1^i 2^j 4^{2k}</math>. Zatem <math>w\in L_2</math> wtedy i tylko wtedy, gdy <math>w'\in L_1</math>. Wykazaliśmy, że <math>L_2 \propto L_1</math>.


Warunek <math>\displaystyle L_1 \propto L_2</math> otrzymujemy w sposób identyczny. Trzeba
Warunek <math>L_1 \propto L_2</math> otrzymujemy w sposób identyczny. Trzeba
tylko wypisać odpowiednią ilość symboli <math>\displaystyle 4</math> (a wiemy już, jak
tylko wypisać odpowiednią ilość symboli <math>4</math> (a wiemy już, jak
konstruować liczbę <math>\displaystyle 2n</math>, mając dane <math>\displaystyle n</math>).
konstruować liczbę <math>2n</math>, mając dane <math>n</math>).

Aktualna wersja na dzień 21:37, 11 wrz 2023

Sformułujemy definicje podstawowych klas złożoności w języku maszyn Turinga oraz metodę ich porównywania. Przeanalizujemy związki między rodziną języków określonych przez maszyny Turinga a rodziną języków typu (0) z hierarchii Chomsky'ego. Podamy dalsze własności języków kontekstowych i typu (0). Wprowadzimy pojęcie języka rekurencyjnie przeliczalnego oraz przedstawimy tezę Churcha. Następnie omówimy teoretyczne podstawy teorii rozstrzygalności oraz przeanalizujemy kilka problemów nierozstrzygalnych w teorii języków.


Klasy złożoności obliczeniowej

Jednym z podstawowych celów wprowadzania maszyn Turinga jest dążenie do formalnej definicji złożoności obliczeniowej. Na podstawie wcześniejszych uwag możemy utożsamiać akceptację słowa przez maszynę Turinga z jej zatrzymaniem się. Intuicyjnie, można takie zachowanie maszyny Turinga utożsamić z wykonaniem programu, który zwraca odpowiedź "Tak" na postawione przez nas pytanie.

Definicja 1.1

Ustalmy funkcje t,s:. Mówimy, że maszyna Turinga 𝒯 (deterministyczna lub niedeterministyczna) akceptuje słowo wΣI* w czasie t(|w|), jeśli istnieje ciąg kt(|w|) konfiguracji d1,d2,,dk takich, że d1=s0w, dk=w1sFw2 dla pewnych w1,w2ΣT*,sFSF oraz didi+1 dla i=1,,k1.

Jeśli istnieje ciąg konfiguracji d1d2dm, gdzie d1=s0w, dm jest konfiguracją akceptującą (tzn. dm=w1sFw2 dla pewnych w1,w2ΣT*,sFSF) oraz dodatkowo |di|s(|w|)+2, to mówimy, że maszyna 𝒯 akceptuje słowo wΣI* w pamięci s(|w|).

Mówimy, że język L jest akceptowany w czasie t(n) (pamięci s(n)), jeśli istnieje maszyna Turinga 𝒯, dla której L(𝒯)=L oraz każde słowo wL jest akceptowane w czasie t(|w|) (pamięci s(|w|) odpowiednio).

Uwaga 1.1

W niektórych podejściach wykorzystuje się, do definicji złożoności pamięciowej, tak zwanych maszyn Turinga off-line. Pomysł polega na tym, aby nie uwzględniać komórek taśmy, z których maszyna czytała informacje, a jedynie te, do których następował zapis. Dzięki temu zabiegowi można w sposób "rozsądny" mówić o akceptacji słowa w pamięci logn itp. W ujęciu prezentowanym w tym wykładzie zajmujemy się akceptacją w pamięci nk, dla k1, zatem nie ma potrzeby dodatkowego definiowania maszyn Turinga off-line.

Definicja 1.2

Oznaczmy przez Dtime(t(n)) oraz Dspace(s(n)) rodzinę języków akceptowanych w czasie t(n) i odpowiednio pamięci s(n) przez deterministyczną maszynę Turinga. Dla maszyn niedeterministycznych wprowadzamy w identyczny sposób klasy Ntime(t(n)) oraz Nspace(s(n)).

Określamy następujące klasy złożoności (klasy języków):

𝐏=k=0Dtime(nk),𝐍𝐏=k=0Ntime(nk),𝐏𝐒𝐏𝐀𝐂𝐄=k=0Dspace(nk),𝐍𝐒𝐏𝐀𝐂𝐄=k=0Nspace(nk).

Wprost z definicji otrzymujemy zależności P NP oraz PSPACE NPSPACE . W dalszej części wykładu udowodnimy kilka mniej oczywistych zależności.

Przykład 1.1

Rozważmy język:

Parser nie mógł rozpoznać (błąd składni): {\displaystyle L = \left\{1^i 2^j 3^k: k=i\cdot j,: i,j\geqslant 1\}\right}

Język L P . Deterministyczna maszyna Turinga MT3 akceptująca taki język może wyglądać następująco (zaczynamy od konfiguracji s0w):

  1. Jeśli symbol pod głowicą, to 1 zamień go na . Inaczej odrzuć.
  2. Przejdź od lewego ogranicznika do prawego, sprawdzając, czy po 1 występuje 1 lub 2, po 2 tylko 2 lub 3, a po 3 kolejny symbol 3 lub ogranicznik. Jeśli ta zależność nie jest spełniona, odrzuć. Gdy osiągniesz ogranicznik wykonaj następny krok.
  3. Gdy przed ogranicznikiem nie znajduje się symbol 3, odrzuć. W przeciwnym razie zamień symbol 3 na , a następnie poruszaj się w lewo, aż dotrzesz do symbolu innego niż 3 i .
  4. Jeśli symbol do którego dotarłeś to 2, zamień go na . Sprawdź symbol po lewej. Jeśli to 2, poruszaj się w prawo aż do ogranicznika. Następnie przejdź do kroku 3.
  5. Jeśli dotarłeś do symbolu 1, poruszaj się w lewo aż do ogranicznika. Zamień symbol 1 przy ograniczniku na , a następnie idź w prawo, zamieniając wszystkie symbole na 2. Gdy dojdziesz do ogranicznika, przejdź do kroku 3.
  6. Jeśli dotarłeś do ogranicznika, oznacza to, że skasowano już wszystkie symbole 1. Przejdź w prawo aż do ogranicznika. Jeśli natrafisz na symbol 3, odrzuć. W przeciwnym przypadku, akceptuj.

Nietrudno zaobserwować, że maszyna MT3 przechodzi przez taśmę w prawo i w lewo tyle razy, ile symboli 3 zawiera taśma oraz wykonuje jeden dodatkowy przebieg na starcie. Zatem słowa z L są akceptowane w czasie ograniczonym wielomianowo.

Przykład 1.2

Rozważmy teraz język

L={3k:k=ij dla pewnych i,j>1}

Najprostszą metodą uzasadnienia, że L NP jest konstrukcja tak zwanej wyroczni. Polega ona na następującej dwuetapowej procedurze:

  1. Skonstruuj niedeterministyczną maszynę Turinga (wyrocznia) generującą pewne słowo (certyfikat).
  2. Zweryfikuj w sposób deterministyczny spełnienie założeń przez certyfikat.

W naszym przykładzie Etap 1 wygląda następująco:

  1. Użyj dwóch taśm. Na pierwszej z nich znajduje się 3k.
  2. Idź po pierwszej taśmie, wykorzystując niedeterministyczną funkcję przejść. Napotykając 3, możesz wypisać 1 na taśmie drugiej i przejść o jedną komórkę w prawo na taśmie pierwszej lub przejść do następnego kroku. Jeśli dotarłeś do prawego ogranicznika taśmy pierwszej, przejdź do kroku 3.
  3. Powróć do początku pierwszej taśmy. Wykonaj sekwencję jak w kroku 2, z tą różnicą, że teraz na drugiej taśmie wypisuj symbole 2.
  4. Jako ostatnią część tego etapu przekopiuj symbole 3 z pierwszej taśmy na drugą (po symbolach 1 i 2).

W konstrukcji wykorzystaliśmy dwie taśmy, ale oczywiście w nawiązaniu do wcześniejszych uwag, całą konstrukcję można wykonać na jednej taśmie (z odpowiednio rozszerzonym alfabetem i bardziej skomplikowaną funkcją przejść).

Etap 2 polega na weryfikacji, czy na taśmie drugiej znajduje się słowo postaci 1i2j3k, gdzie i,j>1 oraz k=ij. Jeśli tak, to słowo wejściowe 3k pochodziło z języka L i akceptujemy. Można do tego wykorzystać deterministyczną maszynę Turinga, niemal identyczną z opisaną w przykładzie poprzednim.

Jeśli słowo wejściowe pochodzi z języka L, to jedno z obliczeń maszyny niedeterministycznej z Etapu 1. prowadzi do konstrukcji odpowiedniego słowa na drugiej taśmie. Nie wiemy, jaka dokładnie ścieżka obliczeń ma być wykorzystana, ale dla akceptacji języka L nie ma to znaczenia.

Zastanów się, czy da się wykazać, że także L P (Ćwiczenie 1.3, do tego wykładu).

Definicja 1.3

Funkcja s(n) jest konstruowalna pamięciowo, jeśli istnieje maszyna Turinga 𝒯=(ΣT,S,f,s0,SF), dla której d1*d2, gdzie d1=s01n, d2=s11s(n)w dla s1SF, w(ΣT{1})* oraz dodatkowo d2 jest konfiguracją końcową.

Inaczej mówiąc, funkcję s(n) nazywamy konstruowalną pamięciowo, jeśli istnieje maszyna Turinga 𝒯, otrzymując na wejściu słowo w długości |w|=n, zaznacza na taśmie roboczej s(n) klatek i zatrzymuje się (akceptując słowo w).

Przykład 1.3

Funkcja s(n)=2n jest konstruowalna pamięciowo. Maszyna MT4, która konstruuje s(n) działa według schematu:

  1. Przejdź do prawego markera. Jeśli napotkano symbol inny niż 1, to odrzuć.
  2. Idź w lewo aż do pierwszego symbolu 1 lub markera
  3. Jeśli napotkałeś symbol 1, zamień go na i przejdź do prawego markera. Dopisz do słowa symbol (zwiększając tym samym długość słowa na taśmie o 1). Następnie powtórz cykl od 2.
  4. Jeśli napotkałeś marker, idź w prawo, zamieniając wszystkie wystąpienia na 1. Następnie wracaj do lewego markera i zatrzymaj się, akceptując.

Twierdzenie 1.1 liniowa kompresja pamięci

Niech będzie dany język L oraz maszyna Turinga 𝒯 akceptująca L w pamięci s(n). Dla dowolnego ε>0 istnieje maszyna Turinga 𝒯 akceptująca L w pamięci max{n,εs(n)}.

Dowód

(Szkic) Ustalamy liczbę naturalną k, dla której εk2. Maszynę 𝒯 definiujemy następująco:

  1. Przekoduj słowo wejściowe, łącząc po r kolejnych symboli w jeden blok stanowiący nowy symbol na taśmie.
  2. Symuluj maszynę 𝒯 na skompresowanej taśmie. Położenie głowicy wewnątrz bloku zakoduj w stanach maszyny 𝒯.

Zauważmy, że w kroku 1. maszyna 𝒯 wykorzystuje n komórek pamięci do odczytania słowa wejściowego. Kompresja taśmy zapewnia, że podczas symulowania maszyny 𝒯 nie wykorzystamy więcej niż s(n)kεs(n) komórek. Jednocześnie można założyć, że 𝒯 akceptuje słowa wejściowe z języka L o długości mniejszej niż k bez symulowania 𝒯.

Twierdzenie 1.2 Savitch

Dla dowolnej funkcji s(n) konstruowalnej pamięciowo spełniającej warunek s(n)log2n prawdziwa jest inkluzja Nspace(s(n))DSpace(s2(n)).

Dowód

Niech 𝒩𝒯 będzie niedeterministyczną maszyną Turinga akceptującą język L=L(𝒩𝒯) w pamięci s(n). Niech k(n) oznacza liczbę konfiguracji potrzebną do zaakceptowania słowa o długości n. Istnieje liczba c>1, dla której k(n)cs(n), co z kolei oznacza, że każde słowo o długości n jest akceptowane w cs(n) krokach czasowych.

Rozważmy algorytm:

Algorytm


  1  Wejście: słowo w długości |w|=n
  2  oblicz s(n)
  3  for każda konfiguracja akceptująca dA dla której |dA|s(n)
  4    do if Test(s0w, dA, s(n)log2c) then akceptuj

gdzie procedura Test ma następującą postać:

Algorytm Procedure Test(d,d,i)


  1  if i=0 and [ (d=d) or (dd)] then return true
  2    else for każda konfiguracja d dla której |d|s(n)
  3      do if Test(d,d,i1) and Test d,d,i1)
  4        then return true;
  5  return false

Przedstawiony algorytm można zrealizować za pomocą wielotaśmowej maszyny Turinga. Założenie dotyczące konstruowalności pamięciowej jest istotnie wykorzystywane w tej konstrukcji przy implementacji linii 3 algorytmu i linii 2 procedury Test. Musimy zaznaczyć s(n) komórek taśmy, aby móc konstruować konfiguracje o długości ograniczonej przez s(n) i móc następnie wykonywać na nich symulację maszyny 𝒩𝒯.

Zauważmy, że ilość konfiguracji jest ograniczona przez s(n), a głębokość rekursji przez logcs(n). Oznacza to, że jesteśmy w stanie skonstruować maszynę Turinga, która wymaga cs2(n) pamięci, gdzie c jest pewną stałą. Na mocy Twierdzenia 1.1 jesteśmy w stanie określić maszynę 𝒯 działającą w pamięci s2(n).

Wniosek 1.1

PSPACE = NPSPACE

Lemat 1.1

Jeśli g(n)n, to Dtime(g(n))Dspace(g(n)) oraz Ntime(g(n))Nspace(g(n)).

Dowód

Niech będzie dana maszyna deterministyczna 𝒯 akceptująca dany język L w czasie g(n). Do akceptacji słowa w o długości n maszyna wykorzystuje co najwyżej g(n) kroków czasowych, czyli odwiedza co najwyżej g(n)+1 komórek taśmy.

Na podstawie Twierdzenia 1.1 istnieje maszyna Turinga 𝒯 wykorzystująca

max{n,12(g(n)+1)}g(n)

komórek pamięci. Dla niedeterministycznych maszyn Turinga argumentacja jest identyczna.

Wniosek 1.2

P NP PSPACE = NPSPACE
Uwaga 1.2

Nie jest znany przykład wykazujący silną inkluzję P NP ani dowód wykluczający istnienie takiego przykładu. Powszechnie uznawana hipoteza głosi:

P NP.

Rozstrzygnięcie jej prawdziwości lub fałszywości stanowi jeden z najważniejszych, a zarazem najtrudniejszych problemów współczesnej informatyki. Jak widzieliśmy w Przykładzie 1.2, nawet w przypadku konkretnego języka L NP, problem uzasadnienia, że także L P, jest nietrywialny, gdyż wymaga zazwyczaj konstrukcji całkiem nowej maszyny Turinga niż ta do weryfikacji L NP .

Redukcja i problemy zupełne

Definicja 2.1 transformacja wielomianowa

Niech L1,L2 będą dowolnymi językami nad pewnym alfabetem ΣI. Mówimy, że L1 redukuje się do L2 w czasie wielomianowym, co oznaczamy L1L2, gdy istnieje deterministyczna maszyna Turinga 𝒯=(ΣT,S,f,s0,SF) taka, że dla dowolnego wΣI* istnieje wΣI* i stan s1SF o własności

s0w*s1w

oraz

wL1wL2

Lemat 2.1

Załóżmy, że L1L2. Wtedy zachodzą implikacje:

  1. L2 P      L1 P,
  2. L2 NP      L1 NP,
  3. L2 PSPACE      L1 PSPACE.

Dowód

Dane słowo w transformujemy do w w czasie wielomianowym, co gwarantuje założenie L1L2. Dzięki założeniu L2 P możemy rozstrzygnąć, czy wL2 (tzn. jeśli akceptujemy w, to robimy to w czasie wielomianowym). Tym sposobem (korzystając z definicji transformacji wielomianowej) akceptujemy w w czasie wielomianowym, o ile tylko wL1. Dowód dla pozostałych implikacji jest identyczny.

Definicja 2.2

Niech 𝒞 oznacza pewną klasę języków. Język L nazywamy 𝒞-trudnym, jeśli spełniony jest warunek:

L𝒞LL

Jeżeli dodatkowo spełniony jest warunek L𝒞, to język L nazywamy 𝒞-zupełnym.

Intuicyjnie, fakt, że język jest 𝒞-zupełny, oznacza, że jest on najbardziej skomplikowany (pod względem obliczeniowym) wśród języków z klasy 𝒞, natomiast język 𝒞-trudny jest bardziej skomplikowany niż każdy z klasy 𝒞, choć sam nie musi do niej należeć.

Uwaga 2.1

Rozważając klasę P , NP i PSPACE, możemy mówić o językach (problemach) P -zupełnych, NP -zupełnych, czy też PSPACE -zupełnych. To samo odnosi się do języków trudnych (tzn. klasa języków P -trudnych, itd.).

Przykład 2.1

Rozważmy języki:

L1={1i2j3k:k=ij,:i,j1},L2={1i2j42k:k=ij,:i,j1}

Języki: L1 oraz L2 wyglądają na bardzo podobne, zatem wydaje się, że L1L2 oraz L2L1. Uzasadnienie tego faktu jest prawie natychmiastowe.

Konstruujemy deterministyczną maszynę Turinga która działa w następujący sposób:

  1. Jeśli słowo wejściowe jest puste, to stop.
  2. Przejdź do prawego ogranicznika. Jeśli przy ograniczniku nie występuje symbol 4, to wykonaj krok 1.
  3. Jeśli w słowie wejściowym występuje symbol 4, to sprawdź, czy słowo przetwarzane jest postaci w4s, gdzie s1 oraz w(ΣI{4})* oraz czy dodatkowo s jest liczbą parzystą. Jeśli nie, to wykonaj krok 1.
  4. Zamień słowo 4s na słowo 3s2s2 i wykonaj krok 1.
  5. Przejdź nad pierwszy symbol po lewym ograniczniku i zatrzymaj się.

W ten sposób zawsze przeprowadzamy konfigurację s0w na konfigurację s1w, przy czym w=1i2j3k tylko, gdy w=1i2j42k. Zatem wL2 wtedy i tylko wtedy, gdy wL1. Wykazaliśmy, że L2L1.

Warunek L1L2 otrzymujemy w sposób identyczny. Trzeba tylko wypisać odpowiednią ilość symboli 4 (a wiemy już, jak konstruować liczbę 2n, mając dane n).