Języki, automaty i obliczenia/Wykład 13: Złożoność obliczeniowa.: Różnice pomiędzy wersjami
m Zastępowanie tekstu – „.↵</math>” na „</math>” |
|||
(Nie pokazano 13 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> | Ustalmy funkcje <math>t,s:\mathbb{N}\rightarrow \mathbb{N}</math>. Mówimy, że | ||
maszyna Turinga <math> | maszyna Turinga <math>\mathcal{MT}</math> (deterministyczna lub | ||
niedeterministyczna) '''akceptuje słowo <math> | niedeterministyczna) '''akceptuje słowo <math>w\in \Sigma_I^*</math> w | ||
czasie <math> | czasie <math>t(|w|)</math>''', jeśli istnieje ciąg <math>k\leqslant t(|w|)</math> konfiguracji | ||
<math> | <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> | \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> | _{T}^{*},s_{F}\in S_{F}</math> oraz <math>d_i \mapsto d_{i+1}</math> dla | ||
<math> | <math>i=1,\dots,k-1</math>. | ||
Jeśli istnieje ciąg konfiguracji <math> | Jeśli istnieje ciąg konfiguracji <math>d_1 \mapsto d_2 \mapsto \dots | ||
\mapsto d_m</math>, gdzie <math> | \mapsto d_m</math>, gdzie <math>d_1=\sharp s_0 w \sharp</math>, <math>d_m</math> jest | ||
konfiguracją akceptującą (tzn. <math> | konfiguracją akceptującą (tzn. <math>d_m= \sharp w_{1}s_{F}w_{2}\sharp</math> | ||
dla pewnych <math> | dla pewnych <math>w_{1},w_{2}\in \Sigma _{T}^{*},s_{F}\in S_{F}</math>) oraz | ||
dodatkowo <math> | dodatkowo <math>|d_i|\leqslant s(|w|)+2</math>, to mówimy, że maszyna <math>\mathcal{MT}</math> | ||
'''akceptuje słowo <math> | '''akceptuje słowo <math>w\in \Sigma_I^*</math> w pamięci <math>s(|w|)</math>'''. | ||
Mówimy, że '''język <math> | Mówimy, że '''język <math>L</math> jest akceptowany w czasie <math>t(n)</math> | ||
(pamięci <math> | (pamięci <math>s(n)</math>)''', jeśli istnieje maszyna Turinga <math>\mathcal{MT}</math>, dla | ||
której <math> | której <math>L(\mathcal{MT})=L</math> oraz każde słowo <math>w\in L</math> jest | ||
akceptowane w czasie <math> | 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> | pamięci <math>\log n</math> itp. W ujęciu prezentowanym w tym wykładzie | ||
zajmujemy się akceptacją w pamięci <math> | 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> | Oznaczmy przez <math>Dtime(t(n))</math> oraz <math>Dspace(s(n))</math> rodzinę języków | ||
akceptowanych w czasie <math> | 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> | wprowadzamy w identyczny sposób klasy <math>Ntime(t(n))</math> oraz | ||
<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>\ | <center><math>\begin{align} \mathbf{P} =\bigcup_{k=0}^\infty Dtime(n^k), &\qquad\qquad& | ||
\ | \mathbf{NP} =\bigcup_{k=0}^\infty Ntime(n^k), | ||
\\ | \\ | ||
\ | \mathbf{PSPACE} =\bigcup_{k=0}^\infty Dspace(n^k), && | ||
\ | \mathbf{NSPACE} =\bigcup_{k=0}^\infty Nspace(n^k). | ||
\ | \end{align}</math></center> | ||
}} | }} | ||
Wprost z definicji otrzymujemy zależności '''P''' <math> | 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> | <center><math> | ||
L=\left\{1^i 2^j 3^k | L = \left\{1^i 2^j 3^k: k=i\cdot j,: i,j\geqslant 1\}\right</math></center> | ||
</math></center> | |||
Język <math> | Język <math>L\in</math> '''P''' . Deterministyczna maszyna Turinga | ||
<math> | <math>MT_3</math> akceptująca taki język może wyglądać następująco (zaczynamy | ||
od konfiguracji <math> | od konfiguracji <math>\sharp s_0 w \sharp</math>): | ||
# Jeśli symbol pod głowicą, to <math> | # 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> | # 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> | # 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> | # 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> | # 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> | # 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> | Nietrudno zaobserwować, że maszyna <math>MT_3</math> przechodzi przez taśmę w prawo i w lewo tyle | ||
razy, ile symboli <math> | razy, ile symboli <math>3</math> zawiera taśma oraz wykonuje jeden dodatkowy | ||
przebieg na starcie. Zatem słowa z <math> | 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> | <center><math> | ||
L= | L = \{ 3^k:k=i\cdot j \text{ dla pewnych }i,j> 1\}</math></center> | ||
</math></center> | |||
Najprostszą metodą uzasadnienia, że <math> | 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> | # 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> | # 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> | # 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> | # 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> | 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> | 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> | 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> | 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> | której <math>d_1 \mapsto^* d_2</math>, gdzie <math>d_1=\sharp s_0 1^n \sharp</math>, | ||
<math> | <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> | (\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> | 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> | 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> | Funkcja <math>s(n)=2n</math> jest konstruowalna pamięciowo. Maszyna <math>MT_4</math>, | ||
która konstruuje <math> | która konstruuje <math>s(n)</math> działa według schematu: | ||
# Przejdź do prawego markera. Jeśli napotkano symbol inny niż <math> | # Przejdź do prawego markera. Jeśli napotkano symbol inny niż <math>1</math>, to odrzuć. | ||
# Idź w lewo aż do pierwszego symbolu <math> | # Idź w lewo aż do pierwszego symbolu <math>1</math> lub markera <math>\sharp</math> | ||
# Jeśli napotkałeś symbol <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> | # 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> | Niech będzie dany język <math>L</math> oraz maszyna Turinga | ||
<math> | <math>\mathcal{TM}</math> akceptująca <math>L</math> w pamięci <math>s(n)</math>. Dla dowolnego | ||
<math> | <math>\varepsilon>0</math> istnieje maszyna Turinga <math>\mathcal{TM}'</math> akceptująca <math>L</math> w | ||
pamięci <math> | pamięci <math>\max\left\{n,\varepsilon s(n)\right\}</math>. | ||
}} | }} | ||
{{dowod||| | {{dowod||| | ||
(''Szkic'') | (''Szkic'') | ||
Ustalamy liczbę naturalną <math> | Ustalamy liczbę naturalną <math>k</math>, dla której <math>\varepsilon k\geqslant 2</math>. Maszynę | ||
<math> | <math>\mathcal{TM}'</math> definiujemy następująco: | ||
# Przekoduj słowo wejściowe, łącząc po <math> | # 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> | # 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> | 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> | zapewnia, że podczas symulowania maszyny <math>\mathcal{MT}</math> nie | ||
wykorzystamy więcej niż <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> | komórek. Jednocześnie można założyć, że <math>\mathcal{MT}'</math> akceptuje | ||
słowa wejściowe z języka <math> | słowa wejściowe z języka <math>L</math> o długości mniejszej niż <math>k</math> bez | ||
symulowania <math> | symulowania <math>\mathcal{MT}</math>. | ||
}} | }} | ||
{{twierdzenie|1.2 Savitch|| | {{twierdzenie|1.2 Savitch|| | ||
Dla dowolnej funkcji <math> | Dla dowolnej funkcji <math>s(n)</math> konstruowalnej pamięciowo spełniającej | ||
warunek <math> | warunek <math>s(n)\geqslant \log_2 n</math> prawdziwa jest inkluzja | ||
<math> | <math>Nspace(s(n))\subset DSpace(s^2(n))</math>. | ||
}} | }} | ||
{{dowod||| | {{dowod||| | ||
Niech <math> | Niech <math>\mathcal{NMT}</math> będzie niedeterministyczną maszyną Turinga | ||
akceptującą język <math> | akceptującą język <math>L=L(\mathcal{NMT})</math> w pamięci <math>s(n)</math>. Niech | ||
<math> | <math>k(n)</math> oznacza liczbę konfiguracji potrzebną do zaakceptowania słowa | ||
o długości <math> | 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> | 1 Wejście: słowo <math>w</math> długości <math>|w|=n</math> | ||
2 oblicz <math> | 2 oblicz <math>s(n)</math> | ||
3 '''for''' każda konfiguracja akceptująca <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> | 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> | {{algorytm|'''Procedure''' Test(<math>d</math>,<math>d'</math>,<math>i</math>)|| | ||
1 '''if''' <math> | 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> | 2 '''else for''' każda konfiguracja <math>d''</math> dla której <math>|d''|\leqslant s(n)</math> | ||
3 '''do if''' Test(<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> | 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> | ograniczonej przez <math>s(n)</math> i móc następnie wykonywać na nich | ||
symulację maszyny <math> | symulację maszyny <math>\mathcal{NMT}</math>. | ||
Zauważmy, że ilość konfiguracji jest ograniczona przez <math> | Zauważmy, że ilość konfiguracji jest ograniczona przez <math>s(n)</math>, a | ||
głębokość rekursji przez <math> | głębokość rekursji przez <math>\log c^{s(n)}</math>. Oznacza to, że jesteśmy w | ||
stanie skonstruować maszynę Turinga, która wymaga <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> | <center> '''PSPACE''' <math>=</math> '''NPSPACE''' </center> | ||
}} | }} | ||
{{lemat|1.1|| | {{lemat|1.1|| | ||
Jeśli <math> | Jeśli <math>g(n)\geqslant n</math>, to <math>Dtime(g(n))\subset Dspace(g(n))</math> oraz | ||
<math> | <math>Ntime(g(n))\subset Nspace(g(n))</math>. | ||
}} | }} | ||
{{dowod||| | {{dowod||| | ||
Niech będzie dana maszyna deterministyczna <math> | Niech będzie dana maszyna deterministyczna <math>\mathcal{MT}</math> | ||
akceptująca dany język <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> | 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> | 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> | <math>\mathcal{MT}'</math> wykorzystująca | ||
<center><math> | <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> | <center> '''P''' <math>\subset</math> '''NP''' <math>\subset | ||
</math> '''PSPACE''' <math> | </math> '''PSPACE''' <math>=</math> '''NPSPACE''' </center> | ||
}} | }} | ||
{{uwaga|1.2|| | {{uwaga|1.2|| | ||
Nie jest znany przykład wykazujący silną inkluzję '''P''' <math> | 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> | <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> | przypadku konkretnego języka <math>L\in</math> '''NP''', problem | ||
uzasadnienia, że także <math> | 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> | ta do weryfikacji <math>L\in</math> '''NP''' . | ||
}} | }} | ||
Linia 267: | Linia 267: | ||
{{definicja|2.1 transformacja wielomianowa|| | {{definicja|2.1 transformacja wielomianowa|| | ||
Niech <math> | Niech <math>L_1,L_2</math> będą dowolnymi językami nad pewnym alfabetem | ||
<math> | <math>\Sigma_I</math>. Mówimy, że <math>L_1</math> redukuje się do <math>L_2</math> w czasie | ||
wielomianowym, co oznaczamy <math> | wielomianowym, co oznaczamy <math>L_1 \propto L_2</math>, gdy istnieje | ||
deterministyczna maszyna Turinga <math> | deterministyczna maszyna Turinga <math>\mathcal{MT}=(\Sigma | ||
_{T},S,f,s_{0},S_{F})</math> taka, że dla dowolnego <math> | _{T},S,f,s_{0},S_{F})</math> taka, że dla dowolnego <math>w\in \Sigma_I^*</math> | ||
istnieje <math> | istnieje <math>w'\in \Sigma_I^*</math> i stan <math>s_1\in S_F</math> o własności | ||
<center><math> | <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> | <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> | Załóżmy, że <math>L_1 \propto L_2</math>. Wtedy zachodzą implikacje: | ||
# <math> | # <math>L_2 \in</math> '''P''' <math>\quad \Longrightarrow \quad L_1 \in</math> '''P''', | ||
# <math> | # <math>L_2 \in</math> '''NP''' <math>\quad \Longrightarrow \quad L_1 \in</math> '''NP''', | ||
# <math> | # <math>L_2 \in</math> '''PSPACE''' <math>\quad \Longrightarrow \quad L_1 \in</math> '''PSPACE'''. | ||
}} | }} | ||
{{dowod||| | {{dowod||| | ||
Dane słowo <math> | 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> | 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> | <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> | 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> | 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> | <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> | <center><math> | ||
L_1=\left\{1^i 2^j 3^k | 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} | 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> | 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> | # 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> | # 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> | # 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> | W ten sposób zawsze przeprowadzamy konfigurację <math>\sharp s_0 w \sharp | ||
</math> na konfigurację <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> | 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>L_1 \propto L_2</math> otrzymujemy w sposób identyczny. Trzeba | |||
tylko wypisać odpowiednią ilość symboli <math>4</math> (a wiemy już, jak | |||
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 . Mówimy, że maszyna Turinga (deterministyczna lub niedeterministyczna) akceptuje słowo w czasie , jeśli istnieje ciąg konfiguracji takich, że , dla pewnych oraz dla .
Jeśli istnieje ciąg konfiguracji , gdzie , jest konfiguracją akceptującą (tzn. dla pewnych ) oraz dodatkowo , to mówimy, że maszyna akceptuje słowo w pamięci .
Mówimy, że język jest akceptowany w czasie (pamięci ), jeśli istnieje maszyna Turinga , dla której oraz każde słowo jest akceptowane w czasie (pamięci odpowiednio).
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 itp. W ujęciu prezentowanym w tym wykładzie zajmujemy się akceptacją w pamięci , dla , zatem nie ma potrzeby dodatkowego definiowania maszyn Turinga off-line.
Definicja 1.2
Oznaczmy przez oraz rodzinę języków akceptowanych w czasie i odpowiednio pamięci przez deterministyczną maszynę Turinga. Dla maszyn niedeterministycznych wprowadzamy w identyczny sposób klasy oraz .
Określamy następujące klasy złożoności (klasy języków):
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:
Język P . Deterministyczna maszyna Turinga akceptująca taki język może wyglądać następująco (zaczynamy od konfiguracji ):
- Jeśli symbol pod głowicą, to zamień go na . Inaczej odrzuć.
- Przejdź od lewego ogranicznika do prawego, sprawdzając, czy po występuje lub , po tylko lub , a po kolejny symbol 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 , odrzuć. W przeciwnym razie zamień symbol na , a następnie poruszaj się w lewo, aż dotrzesz do symbolu innego niż i .
- Jeśli symbol do którego dotarłeś to , zamień go na . Sprawdź symbol po lewej. Jeśli to , poruszaj się w prawo aż do ogranicznika. Następnie przejdź do kroku 3.
- Jeśli dotarłeś do symbolu , poruszaj się w lewo aż do ogranicznika. Zamień symbol przy ograniczniku na , a następnie idź w prawo, zamieniając wszystkie symbole na . Gdy dojdziesz do ogranicznika, przejdź do kroku .
- Jeśli dotarłeś do ogranicznika, oznacza to, że skasowano już wszystkie symbole . Przejdź w prawo aż do ogranicznika. Jeśli natrafisz na symbol , odrzuć. W przeciwnym przypadku, akceptuj.
Nietrudno zaobserwować, że maszyna przechodzi przez taśmę w prawo i w lewo tyle razy, ile symboli zawiera taśma oraz wykonuje jeden dodatkowy przebieg na starcie. Zatem słowa z są akceptowane w czasie ograniczonym wielomianowo.
Przykład 1.2
Rozważmy teraz język
Najprostszą metodą uzasadnienia, że NP jest konstrukcja tak zwanej wyroczni. Polega ona na następującej dwuetapowej procedurze:
- Skonstruuj niedeterministyczną maszynę Turinga (wyrocznia) generującą pewne słowo (certyfikat).
- Zweryfikuj w sposób deterministyczny spełnienie założeń przez certyfikat.
W naszym przykładzie Etap 1 wygląda następująco:
- Użyj dwóch taśm. Na pierwszej z nich znajduje się .
- Idź po pierwszej taśmie, wykorzystując niedeterministyczną funkcję przejść. Napotykając , możesz wypisać 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 , z tą różnicą, że teraz na drugiej taśmie wypisuj symbole .
- Jako ostatnią część tego etapu przekopiuj symbole z pierwszej taśmy na drugą (po symbolach i ).
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 , gdzie oraz . Jeśli tak, to słowo wejściowe pochodziło z języka 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 , 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 nie ma to znaczenia.
Zastanów się, czy da się wykazać, że także P (Ćwiczenie 1.3, do tego wykładu).
Definicja 1.3
Funkcja jest konstruowalna pamięciowo, jeśli istnieje maszyna Turinga , dla której , gdzie , dla , oraz dodatkowo jest konfiguracją końcową.
Inaczej mówiąc, funkcję nazywamy konstruowalną pamięciowo, jeśli istnieje maszyna Turinga , otrzymując na wejściu słowo długości , zaznacza na taśmie roboczej klatek i zatrzymuje się (akceptując słowo ).
Przykład 1.3
Funkcja jest konstruowalna pamięciowo. Maszyna , która konstruuje działa według schematu:
- Przejdź do prawego markera. Jeśli napotkano symbol inny niż , to odrzuć.
- Idź w lewo aż do pierwszego symbolu lub markera
- Jeśli napotkałeś symbol , 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 ). Następnie powtórz cykl od .
- Jeśli napotkałeś marker, idź w prawo, zamieniając wszystkie wystąpienia na . Następnie wracaj do lewego markera i zatrzymaj się, akceptując.
Twierdzenie 1.1 liniowa kompresja pamięci
Niech będzie dany język oraz maszyna Turinga akceptująca w pamięci . Dla dowolnego istnieje maszyna Turinga akceptująca w pamięci .
Dowód
(Szkic) Ustalamy liczbę naturalną , dla której . Maszynę definiujemy następująco:
- Przekoduj słowo wejściowe, łącząc po kolejnych symboli w jeden blok stanowiący nowy symbol na taśmie.
- Symuluj maszynę na skompresowanej taśmie. Położenie głowicy wewnątrz bloku zakoduj w stanach maszyny .
Zauważmy, że w kroku . maszyna wykorzystuje komórek pamięci do odczytania słowa wejściowego. Kompresja taśmy zapewnia, że podczas symulowania maszyny nie wykorzystamy więcej niż komórek. Jednocześnie można założyć, że akceptuje słowa wejściowe z języka o długości mniejszej niż bez symulowania .

Twierdzenie 1.2 Savitch
Dla dowolnej funkcji konstruowalnej pamięciowo spełniającej warunek prawdziwa jest inkluzja .
Dowód
Niech będzie niedeterministyczną maszyną Turinga akceptującą język w pamięci . Niech oznacza liczbę konfiguracji potrzebną do zaakceptowania słowa o długości . Istnieje liczba , dla której , co z kolei oznacza, że każde słowo o długości jest akceptowane w krokach czasowych.

Rozważmy algorytm:
Algorytm
1 Wejście: słowo długości 2 oblicz 3 for każda konfiguracja akceptująca dla której 4 do if Test(, , ) then akceptuj
gdzie procedura Test ma następującą postać:
Algorytm Procedure Test(,,)
1 if and [ () or ()] then return true 2 else for każda konfiguracja dla której 3 do if Test(,,) and Test ,,) 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ć komórek taśmy, aby móc konstruować konfiguracje o długości ograniczonej przez i móc następnie wykonywać na nich symulację maszyny .
Zauważmy, że ilość konfiguracji jest ograniczona przez , a głębokość rekursji przez . Oznacza to, że jesteśmy w stanie skonstruować maszynę Turinga, która wymaga pamięci, gdzie jest pewną stałą. Na mocy Twierdzenia 1.1 jesteśmy w stanie określić maszynę działającą w pamięci .
Wniosek 1.1
Lemat 1.1
Jeśli , to oraz .
Dowód
Niech będzie dana maszyna deterministyczna akceptująca dany język w czasie . Do akceptacji słowa o długości maszyna wykorzystuje co najwyżej kroków czasowych, czyli odwiedza co najwyżej komórek taśmy.
Na podstawie Twierdzenia 1.1 istnieje maszyna Turinga wykorzystująca
komórek pamięci. Dla niedeterministycznych maszyn Turinga argumentacja jest identyczna.

Wniosek 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:
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 NP, problem uzasadnienia, że także P, jest nietrywialny, gdyż wymaga zazwyczaj konstrukcji całkiem nowej maszyny Turinga niż ta do weryfikacji NP .
Redukcja i problemy zupełne
Definicja 2.1 transformacja wielomianowa
Niech będą dowolnymi językami nad pewnym alfabetem . Mówimy, że redukuje się do w czasie wielomianowym, co oznaczamy , gdy istnieje deterministyczna maszyna Turinga taka, że dla dowolnego istnieje i stan o własności
oraz
Lemat 2.1
Załóżmy, że . Wtedy zachodzą implikacje:
- P P,
- NP NP,
- PSPACE PSPACE.
Dowód
Dane słowo transformujemy do w czasie wielomianowym, co gwarantuje założenie . Dzięki założeniu P możemy rozstrzygnąć, czy (tzn. jeśli akceptujemy , to robimy to w czasie wielomianowym). Tym sposobem (korzystając z definicji transformacji wielomianowej) akceptujemy w czasie wielomianowym, o ile tylko . Dowód dla pozostałych implikacji jest identyczny.

Definicja 2.2
Niech oznacza pewną klasę języków. Język nazywamy -trudnym, jeśli spełniony jest warunek:
Jeżeli dodatkowo spełniony jest warunek , to język 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ć.
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:
Języki: oraz wyglądają na bardzo podobne, zatem wydaje się, że oraz . Uzasadnienie tego faktu jest prawie natychmiastowe.
Konstruujemy deterministyczną maszynę Turinga która działa w następujący sposób:
- Jeśli słowo wejściowe jest puste, to stop.
- Przejdź do prawego ogranicznika. Jeśli przy ograniczniku nie występuje symbol , to wykonaj krok 1.
- Jeśli w słowie wejściowym występuje symbol , to sprawdź, czy słowo przetwarzane jest postaci , gdzie oraz oraz czy dodatkowo jest liczbą parzystą. Jeśli nie, to wykonaj krok 1.
- Zamień słowo na słowo i wykonaj krok 1.
- Przejdź nad pierwszy symbol po lewym ograniczniku i zatrzymaj się.
W ten sposób zawsze przeprowadzamy konfigurację na konfigurację , przy czym tylko, gdy . Zatem wtedy i tylko wtedy, gdy . Wykazaliśmy, że .
Warunek otrzymujemy w sposób identyczny. Trzeba tylko wypisać odpowiednią ilość symboli (a wiemy już, jak konstruować liczbę , mając dane ).