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
Matiunreal (dyskusja | edycje)
m Zastępowanie tekstu – „.↵</math>” na „</math>”
 
(Nie pokazano 95 wersji utworzonych przez 4 użytkowników)
Linia 1: Linia 1:
'''Wprowadzenie:''' Sformułujemy definicje podstawowych klas
Sformułujemy definicje podstawowych klas
złożoności w języku maszyn Turinga oraz metodę ich porównywania.
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
Przeanalizujemy związki między rodziną języków określonych przez maszyny Turinga a
Linia 9: Linia 9:
kilka problemów nierozstrzygalnych w teorii języków.
kilka problemów nierozstrzygalnych w teorii języków.


'''Słowa kluczowe:'''
__FORCETOC__
klasy złożoności obliczeniowej, redukcja wielomianowa, problemy zupełne, teza Churcha,
język rekurencyjnie
przeliczalny, zamkniętość na działania, rozstrzygalność, nierozstrzygalność.


==1. Klasy złożoności obliczeniowej==
==Klasy złożoności obliczeniowej==


Jednym z podstawowych celów wprowadzania maszyn Turinga jest dążenie
Jednym z podstawowych celów wprowadzania maszyn Turinga jest dążenie
Linia 21: Linia 18:
przez maszynę Turinga z jej '''zatrzymaniem się'''. Intuicyjnie,
przez maszynę Turinga z jej '''zatrzymaniem się'''. Intuicyjnie,
można takie zachowanie maszyny Turinga utożsamić z wykonaniem
można takie zachowanie maszyny Turinga utożsamić z wykonaniem
programu który zwraca odpowiedź "Tak" na postawione przez nas
programu, który zwraca odpowiedź "Tak" na postawione przez nas
pytanie.
pytanie.


{{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\leq 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|\leq 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).
}}
}}


{{uwaga|1.1||
{{uwaga|1.1||
W niektórych podejściach wykorzystuje się do definicji złożoności
W niektórych podejściach wykorzystuje się, do definicji złożoności
pamięciowej tak zwanych maszyn Turinga off-line. Pomysł polega na
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
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
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\geq 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
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.
'''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.


{{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\geq 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łowica 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>
# 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.
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
# 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>.
symbol <math>\displaystyle 3</math> lub ogranicznik. Jeśli ta zależność nie jest spełniona,
# 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.
odrzuć. Gdy osiągniesz ogranicznik wykonaj następny krok.
# 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>.
# Gdy przed
# 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.
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>.
# 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 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 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.


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.
}}
}}


{{przyklad|1.2||
{{kotwica|prz.1.2|}}{{przyklad|1.2||


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\right\}
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:
# skonstruuj niedeterministyczną maszynę Turinga (wyrocznia) generującą pewne
# Skonstruuj niedeterministyczną maszynę Turinga (wyrocznia) generującą pewne słowo (certyfikat).
słowo (certyfikat)
# Zweryfikuj w sposób deterministyczny spełnienie założeń przez certyfikat.
# zweryfikuj w sposób deterministyczny spełnienie założeń przez
certyfikat


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ą
# 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.
funkcję przejść. Napotykając <math>\displaystyle 3</math> możesz wypisać <math>\displaystyle 1</math> na taśmie
# 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>.
drugiej i przejść o jedną komórkę w prawo na taśmie pierwszej lub
# 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>).
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>.
# 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>)


W konstrukcji wykorzystaliśmy dwie taśmy, ale oczywiście w
W konstrukcji wykorzystaliśmy dwie taśmy, ale oczywiście w
Linia 150: Linia 119:
skomplikowaną funkcją przejść).
skomplikowaną funkcją przejść).


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
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.
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.


Jeśli słowo wejściowe pochodzi z języka <math>\displaystyle L</math> to jedno z obliczeń
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.
maszyny niedeterministycznej z  Etapu pierwszego 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.


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


Inaczej mówiąc funkcję <math>\displaystyle s(n)</math> nazywamy konstruowalną pamięciowo
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
jeśli istnieje maszyna Turinga <math>\displaystyle \mathcal{MT}</math> otrzymując na wejściu
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>).
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>).


{{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ż
# Przejdź do prawego markera. Jeśli napotkano symbol inny niż <math>1</math>, to odrzuć.
<math>\displaystyle 1</math> to odrzuć.
# Idź w lewo aż do pierwszego symbolu <math>1</math> lub markera <math>\sharp</math>
# Idź w lewo aż do pierwszego symbolu <math>\displaystyle 1</math> lub markera <math>\displaystyle \sharp</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ś symbol <math>\displaystyle 1</math> zamień go na <math>\displaystyle \clubsuit</math> i przejdź
# 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.
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ś 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.


}}
}}


{{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\geq 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
# Przekoduj słowo wejściowe, łącząc po <math>r</math> kolejnych symboli w jeden blok stanowiący nowy symbol na taśmie.
jeden blok stanowiący nowy symbol na taśmie.
# 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>.
# 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>


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 \leq \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|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)\geq \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)\leq
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.
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.
 
Rozważmy algorytm:  
Rozważmy algorytm:  


{| border=1
{{algorytm|||
|+ <span style="font-variant:small-caps">Uzupelnij tytul</span>
  1  Wejście: słowo <math>w</math> długości <math>|w|=n</math>
|-
  2  oblicz <math>s(n)</math>
|  ||  '''Algorithm 1'''
  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>\sharp s_0 w \sharp</math>, <math>d_A</math>, <math>s(n) \log_2 c</math>) '''then''' akceptuj
|
}}
1:  || Wejście: słowo <math>\displaystyle w</math> długości <math>\displaystyle |w|=n</math>
|-
| 2:  || oblicz <math>\displaystyle s(n)</math>
|-
| 3: || '''for''' każda konfiguracja akceptująca <math>\displaystyle d_A</math> dla której <math>\displaystyle |d_A|\leq
s(n)</math>
|-
| 4:  || <math>\displaystyle \qquad</math> '''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
|-
|


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


|}
{{algorytm|'''Procedure''' Test(<math>d</math>,<math>d'</math>,<math>i</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>d''</math> dla której <math>|d''|\leqslant s(n)</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''';
  5  '''return false'''
}}


gdzie procedura Test ma następującą postać:
Przedstawiony algorytm można zrealizować za pomocą wielotaśmowej
 
{| border=1
|+ <span style="font-variant:small-caps">Uzupelnij tytul</span>
|-
|  ||  '''procedure''' Test(<math>\displaystyle d</math>,<math>\displaystyle d'</math>,<math>\displaystyle 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'''
|-
| 2: || <math>\displaystyle \qquad</math> '''else for''' każda konfiguracja <math>\displaystyle d''</math> dla której
<math>\displaystyle |d''|\leq s(n)</math>
|-
| 3: ||  <math>\displaystyle \qquad \qquad</math> '''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>)
|-
| 4: || <math>\displaystyle \qquad \qquad \qquad</math> '''then return true''';
|-
| 5: ||  '''return false'''
|-
|
 
|}
 
Przedstawiony algorytm można zrealizować za pomocą wielo-taśmowej
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>
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>.
pamięci, gdzie <math>\displaystyle c'</math> jest pewną stałą. Na mocy
Twierdzenia&nbsp;[[##twTComp|Uzupelnic twTComp|]] jesteśmy w stanie określić maszynę
<math>\displaystyle \mathcal{MT}</math> działającą w pamięci <math>\displaystyle s^2(n)</math>.
}}


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


}}
}}


{{lemat|||
{{lemat|1.1||
Jeśli <math>\displaystyle g(n)\geq 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 conajwyż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 conajwyż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&nbsp;[[##twTComp|Uzupelnic twTComp|]] 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\}\leq g(n)
\max \left\{n, \frac{1}{2}(g(n)+1)\right\}\leqslant g(n)
</math></center>
</math></center>


Linia 328: Linia 243:
}}
}}


{{wniosek|||
{{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|||
{{uwaga|1.2||
Nie jest znany przykład wykazujący silną inkluzję
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
'''P''' <math>\displaystyle  \varsubsetneq </math> '''NP''' ani dowód
hipoteza głosi:  
wykluczający istnienie takiego przykładu. Powszechnie uznawana
<center> '''P''' <math>\neq</math> '''NP'''. </center>
hipoteza głosi:
<center> '''P''' <math>\displaystyle  \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&nbsp;[[##ex_NP|Uzupelnic ex_NP|]] 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
uzasadnienie, ż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'''  .
}}
}}


==2. Redukcja i problemy zupełne==
==Redukcja i problemy zupełne==


{{definicja|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|||
{{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''' <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''' <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''' <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
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.
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.
}}
}}


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


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


Intuicyjnie, fakt że język jest <math>\displaystyle \mathcal{C}</math>-zupełny oznacza że
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
jest ona najbardziej skomplikowany (pod względem obliczeniowym)
<math>\mathcal{C}</math>, choć sam nie musi do niej należeć.
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
<math>\displaystyle \mathcal{C}</math> choć sam nie musi do niej należeć.


{{uwaga|||
{{uwaga|2.1||
Rozważając klasę  '''P''' ,  '''NP'''  i
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
'''PSPACE'''  możemy mówić o językach (problemach)
języków trudnych (tzn. klasa języków  '''P''' -trudnych, itd.).
'''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.).
}}
}}


{{przyklad|||
{{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\geq 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\geq 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:
# 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
# Przejdź do prawego ogranicznika. Jeśli przy ograniczniku nie występuje symbol <math>4</math>, to wykonaj krok [[#prz.1|1]].
występuje symbol <math>\displaystyle 4</math> to wykonaj krok [[##Step_stop_1|Uzupelnic Step_stop_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]].
# 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
# 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]].
4^s \sharp</math> gdzie <math>\displaystyle s\geq 1</math> oraz <math>\displaystyle w\in (\Sigma_I\setminus
# Przejdź nad pierwszy symbol po lewym ograniczniku i zatrzymaj się.
\left\{4\right\})^*</math> oraz czy dodatkowo <math>\displaystyle s</math> jest liczbą parzystą. Jeśli nie
to wykonaj krok [[##Step_stop_1|Uzupelnic Step_stop_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 [[##Step_stop_1|Uzupelnic Step_stop_1|]].
# Przejdź nad pierwszy symbol po lewym ograniczniku i zatrzymaj
się.
 
W ten sposób zawsze przeprowadzamy konfigurację <math>\displaystyle \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>
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>.
 
Warunek <math>\displaystyle 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
konstruować liczbę <math>\displaystyle 2n</math> mając dane <math>\displaystyle n</math>).
}}
 
==3. Języki maszyn Turinga i rodzina <math>\displaystyle \mathcal{L}_{0}</math>==
 
Powstaje naturalne pytanie o związki pomiędzy  klasą
języków rozpoznawanych przez maszyny Turinga, a klasami zadanymi
poprzez gramatyki. Odpowiemy na to pytanie w tej części
wykładu.
 
{{twierdzenie|||
Każdy język akceptowany przez maszynę Turinga jest typu
'''(0)'''.
 
<center><math>\displaystyle \mathcal{L}(MT)\subset \mathcal{L}_{0}</math></center>
 
}}
 
{{dowod|||
Niech  <math>\displaystyle L  </math>  będzie językiem akceptowanym przez maszynę Turinga  <math>\displaystyle \mathbf{MT}=(\Sigma _{T},S,f,s_{0},S_{F})  </math> , o której założymy, że
<math>\displaystyle f(s_{0},\#)=(s',\#,1)  </math> , jeśli para  <math>\displaystyle (s_{0},\#)  </math>  należy do
dziedziny funkcji przejść  <math>\displaystyle f  </math>  maszyny Turinga. Założenie to nie
ogranicza ogólności rozważań. Wyróżnimy pewien podzbiór  <math>\displaystyle \overline{S}_{F}  </math>  zbioru stanów  <math>\displaystyle S  </math> , którego elementy,
jak wskazuje oznaczenie, skojarzone są ze stanami końcowymi. Do
zbioru  <math>\displaystyle \overline{S}_{F}  </math>  należy każdy stan  <math>\displaystyle \overline{s}\in S
</math> , dla którego istnieje ciąg stanów  <math>\displaystyle s_{1}=\overline{s},...,s_{k}  </math>  dla  <math>\displaystyle k\geq 1  </math>  taki, że  <math>\displaystyle (s_{i},\#)\rightarrow (s_{i+1},\#,0)  </math>  dla  <math>\displaystyle k=1,...,k-1  </math>  oraz
<math>\displaystyle (s_{k},\#)\rightarrow (s,\#,1)  </math> , gdzie  <math>\displaystyle s\in S_{F}  </math> .
Zauważmy, iż wraz ze stanem  <math>\displaystyle \overline{s}  </math>  do zbioru  <math>\displaystyle \overline{S}_{F}  </math>  należą wszystkie elementy ciągu  <math>\displaystyle s_{1}=\overline{s},...,s_{k}  </math> .
 
Określamy teraz gramatykę  <math>\displaystyle G=(V_{N},V_{T},v_{0},P)  </math> . Zbiór
symboli nieterminalnych  <math>\displaystyle V_{N}  </math>  zawiera wyłącznie
następujące symbole:
# dla każdego stanu  <math>\displaystyle s\in S  </math>  i  <math>\displaystyle a\in \Sigma _{T}\setminus \{\#\}  </math>  symbole
<math>\displaystyle v_{sa},\: ^{\#}v_{sa},\: v_{sa}^{\#},\: ^{\#}v_{sa}^{\#}  </math>
# dla każdej litery  <math>\displaystyle a\in \Sigma _{T}\setminus \{\#\}  </math>  symbole  <math>\displaystyle \, ^{\#}a  </math>
i  <math>\displaystyle a^{\#}  </math>
# wszystkie elementy zbioru  <math>\displaystyle \Sigma _{T}\setminus (\Sigma _{I}\cup \{\#\})  </math>
# symbole  <math>\displaystyle v_{0},\: v_{1}  </math>  nie należące do  <math>\displaystyle \Sigma _{T}  </math>
 
Zbiór praw  <math>\displaystyle P  </math>  składa się z praw wymienionych poniżej:
#  <math>\displaystyle v_{0}\rightarrow \, ^{\#}v_{sa}^{\#}  </math> ,  <math>\displaystyle v_{0}\rightarrow v_{1}v_{sa}^{\#}  </math>
jeśli  <math>\displaystyle f(s,a)=(\overline{s},b,1)  </math>  dla pewnego  <math>\displaystyle b\in \Sigma
_{T}\setminus \{\#\}  </math> ,
<math>\displaystyle \overline{s}\in \overline{S}_{F}  </math> ,
#  <math>\displaystyle v_{1}\rightarrow \, ^{\#}a  </math> ,  <math>\displaystyle v_{1}\rightarrow v_{1}a  </math>  dla każdego
<math>\displaystyle a\in \Sigma _{T}\setminus \{\#\}  </math> ,
#  <math>\displaystyle v_{s_{1}c}b\rightarrow cv_{sa}  </math>  ,  <math>\displaystyle \, ^{\#}v_{s_{1}c}b\rightarrow \, ^{\#}cv_{sa}  </math>
,  <math>\displaystyle v_{s_{1}c}b^{\#}\rightarrow cv^{\#}_{sa}  </math>  ,  <math>\displaystyle \,
^{\#}v_{s_{1}c}b^{\#}\rightarrow \, ^{\#}cv^{\#}_{sa}  </math>  jeśli  <math>\displaystyle f(s,a)=(s_{1},b,-1)  </math>  i  <math>\displaystyle c\in \Sigma _{T}\setminus \{\#\}  </math> ,
#  <math>\displaystyle bv_{s_{1}c}\rightarrow v_{sa}c  </math>  ,  <math>\displaystyle \, ^{\#}bv_{s_{1}c}\rightarrow \, ^{\#}v_{sa}c  </math>
,  <math>\displaystyle bv_{s_{1}c}^{\#}\rightarrow v_{sa}c^{\#}  </math>  ,  <math>\displaystyle \,
^{\#}bv_{s_{1}c}^{\#}\rightarrow \, ^{\#}v_{sa}c^{\#}  </math>  jeśli  <math>\displaystyle f(s,a)=(s_{1},b,1)  </math>  i  <math>\displaystyle c\in \Sigma _{T}\setminus \{\#\}  </math> ,
#  <math>\displaystyle v_{s_{1}b}\rightarrow v_{sa}  </math>  ,  <math>\displaystyle \, ^{\#}v_{s_{1}b}\rightarrow \, ^{\#}v_{sa}  </math>
,  <math>\displaystyle v_{s_{1}b}^{\#}\rightarrow v^{\#}_{sa}  </math>  ,  <math>\displaystyle \,
^{\#}v_{s_{1}b}^{\#}\rightarrow \, ^{\#}v^{\#}_{sa}  </math>  jeśli  <math>\displaystyle f(s,a)=(s_{1},b,0)  </math> ,
#  <math>\displaystyle v_{s_{1}b}^{\#}\rightarrow v^{\#}_{sa}  </math>  ,  <math>\displaystyle \, ^{\#}v_{s_{1}b}^{\#}\rightarrow \, ^{\#}v^{\#}_{sa}  </math> ,
jeśli istnieją  <math>\displaystyle s_{1}',...,s_{k}'\in S  </math>  dla  <math>\displaystyle k\geq 1  </math>  takie,
że  <math>\displaystyle f(s,a)=(s_{1}',b,1)  </math> ,  <math>\displaystyle f(s_{i}',\#)=(s_{i+1}',\#,0)  </math>  dla
<math>\displaystyle i=1,...,k-1  </math>  oraz  <math>\displaystyle f(s_{k}',\#)=(s_{1},\#,-1)  </math> ,
#  <math>\displaystyle \, ^{\#}v_{s_{1}b}\rightarrow \, ^{\#}v_{sa}  </math>  , ,  <math>\displaystyle \, ^{\#}v_{s_{1}b}^{\#}\rightarrow \, ^{\#}v^{\#}_{sa}  </math> ,
jeśli  istnieją  <math>\displaystyle s_{1}',...,s_{k}'\in S  </math>  dla  <math>\displaystyle k\geq 1  </math>
takie, że  <math>\displaystyle f(s,a)=(s_{1}',b,-1)  </math> ,  <math>\displaystyle f(s_{i}',\#)=(s_{i+1}',\#,0)  </math>  dla  <math>\displaystyle i=1,...,k-1  </math>  oraz  <math>\displaystyle f(s_{k}',\#)=(s_{1},\#,1)  </math> ,
#  <math>\displaystyle a^{\#}\rightarrow a  </math>  dla wszystkich  <math>\displaystyle a\in \Sigma _{T}\setminus \{\#\}  </math> ,
#  <math>\displaystyle \, ^{\#}v_{sa}\rightarrow a  </math>  ,  <math>\displaystyle \, ^{\#}v_{sa}^{\#}\rightarrow a  </math> ,
jeśli  <math>\displaystyle f(s_{0},\#)=(s,\#,1)  </math>  (porównaj założenie na początku
dowodu),
#  <math>\displaystyle v_{0}\Rightarrow 1  </math>  jeśli  <math>\displaystyle 1\in L  </math> .
 
Określona powyżej gramatyka  <math>\displaystyle G  </math>  jest gramatyką typu
'''(0)'''. Rozważmy teraz dowolne słowo  <math>\displaystyle w  </math> , dla którego
istnieje wyprowadzenie w gramatyce  <math>\displaystyle G  </math>  ze stanu
początkowego  <math>\displaystyle v_{0}  </math>  przy użyciu praw 1-7. Słowo  <math>\displaystyle w  </math>  zawiera
dokładnie jeden z następujących symboli  <math>\displaystyle v_{sa},\,
^{\#}v_{sa},v_{sa}^{\#}  </math>  lub  <math>\displaystyle \, ^{\#}v_{sa}^{\#}  </math> . Pierwsza
litera słowa  <math>\displaystyle w  </math>  oznaczona jest markerem  <math>\displaystyle \#  </math>  z lewej
strony, a ostatnia litera słowa  <math>\displaystyle w  </math>  oznaczona jest markerem  <math>\displaystyle \#  </math>  ze strony prawej. Ponadto żadna z liter występujących pomiędzy
pierwszą a ostatnią nie jest oznaczona markerem  <math>\displaystyle \#  </math> . Z każdym
takim słowem kojarzymy konfigurację poprzez zastąpienie symbolu  <math>\displaystyle v_{sa}  </math>  przez  <math>\displaystyle sa  </math>  oraz przez dopisanie symbolu  <math>\displaystyle \#  </math>  po
lewej lub prawej stronie znaczonej przez ten marker litery, zgodnie
z jego występowaniem. Jeśli np.  <math>\displaystyle w=\, ^{\#}v_{sa}^{\#}  </math> , to
skojarzona konfiguracja jest postaci  <math>\displaystyle \#sa\#  </math> . Zauważmy, że
jeśli słowa  <math>\displaystyle u  </math>  i  <math>\displaystyle w  </math>  są w powyższej formie, to fakt, iż  <math>\displaystyle u\mapsto^{*}w  </math> , jest równoważny stwierdzeniu, że z
konfiguracji skojarzonej ze słowem  <math>\displaystyle w  </math>  maszyna Turinga
<math>\displaystyle MT  </math>  może przejść (bezpośrednie następstwo) do
konfiguracji skojarzonej ze słowem  <math>\displaystyle u  </math> . Każdy krok
obliczenia realizowanego przez  <math>\displaystyle MT  </math>  ma swój odpowiednik - krok w
wyprowadzeniu w gramatyce  <math>\displaystyle G  </math> . Z tym, że wobec praw 6 i 7
sekwencja obliczeń
 
<center><math>\displaystyle vsa\#\rightarrow vbs_{1}'\#\rightarrow ...\rightarrow
vbs_{k}'\#\rightarrow vs_{1}b\#</math></center>
 
jest traktowana jako jeden krok w
obliczeniu prowadzonym przez maszynę Turinga. I analogicznie
traktujemy sekwencję z markerem  <math>\displaystyle \#  </math>  występującym po lewej
stronie. Ze stanu początkowego  <math>\displaystyle v_{0}  </math>  gramatyki  <math>\displaystyle G  </math>
można wyprowadzić wszystkie słowa  <math>\displaystyle w  </math> , dla których konfiguracja
jest równa  <math>\displaystyle \#vsa\#  </math>  dla pewnego  <math>\displaystyle v\in (\Sigma _{I}\setminus
\{\#\})^{*}  </math>  oraz maszyna Turinga realizuje obliczenie
 
<center><math>\displaystyle sa\#\rightarrow bs_{1}'\#\rightarrow ...\rightarrow
bs_{k}'\#\rightarrow b\#s_{1},\quad s_{1}\in S_{F}.</math></center>
 
Wynika to z
praw 1 i 2 skonstruowanej gramatyki  <math>\displaystyle G  </math> . Z kolei prawa
typu 9 służą do zastąpienia symboli nieterminalnych typu
<math>\displaystyle \, ^{\#}v_{sa},\, ^{\#}v_{sa}^{\#}  </math>  przez litery terminalne,
a&nbsp;prawa typu 8 eliminują symbole nieterminalne typu  <math>\displaystyle a^{\#}  </math> . A
zatem dla niepustego słowa  <math>\displaystyle w\in (\Sigma _{I}\setminus \{\#\})^{*}
</math> spełniona jest równoważność
 
<center><math>\displaystyle v_{0}\mapsto_{G}^{*}w\: \Leftrightarrow \: s_{0}\#w\#\rightarrow
_{TM}^{*}\#u\#s_{1}, </math></center>
 
gdzie  <math>\displaystyle u\in (\Sigma _{I}\setminus
\{\#\})^{*}  </math> oraz  <math>\displaystyle s_{1}\in S_{F}  </math> . Prawo 10. zapewnia, że
powyższa równoważność prawdziwa jest także dla słowa pustego  <math>\displaystyle 1
</math> . A to kończy dowód tego twierdzenia. }}
 
Język  <math>\displaystyle L  </math>  nazywamy '''rekurencyjnie przeliczalnym'''
jeśli istnieje efektywny
algorytm wyliczający wyłącznie słowa z  <math>\displaystyle L  </math> . Przez efektywny
algorytm rozumiemy skończony zbiór instrukcji, który w jednoznaczny
sposób opisuje działanie tego algorytmu. Klasę języków rekurencyjnie
przeliczalnych oznaczamy symbolem  <math>\displaystyle \mathcal{RP}  </math> .
 
Zauważmy, że każda gramatyka  <math>\displaystyle G  </math>  typu '''(0)''' jest
algorytmem wyliczającym wyłącznie słowa z  <math>\displaystyle L=L(G)  </math> . Dla każdej
liczby naturalnej  <math>\displaystyle k  </math>  można bowiem rozważyć skończony zbiór
wyprowadzeń w  <math>\displaystyle G  </math>  rozpoczynających się od symbolu początkowego
<math>\displaystyle v_{0}  </math>  i o długości równej  <math>\displaystyle k  </math> . Z tego zbioru można z kolei
wybrać wyłącznie te wyprowadzenia, które kończą się słowem
nad alfabetem terminalnym gramatyki  <math>\displaystyle G  </math>  i tylko te
słowa dodawać do listy składającej się na język  <math>\displaystyle L  </math> . Są to, jak
łatwo zauważyć, wszystkie słowa języka  <math>\displaystyle L  </math>  i nic ponadto. A
zatem
 
{{twierdzenie|||
Każdy język typu '''(0)''' jest językiem rekurencyjnie
przeliczalnym, czyli  <math>\displaystyle \mathcal{L}_{0}\subset \mathcal{RP}  </math> .
 
}}
 
Język  <math>\displaystyle L  </math>  nazywamy '''rekurencyjnym'''
jeśli istnieje efektywny algorytm
rozstrzygający dla każdego słowa  <math>\displaystyle w\in A^{*}  </math>  jego
przynależność do języka  <math>\displaystyle L  </math> . Klasę języków
rekurencyjnych oznaczamy symbolem  <math>\displaystyle \mathcal{R}  </math> .
 
Klasa języków kontekstowych zawiera się istotnie w klasie
języków rekurencyjnych, o czym przekonuje poniższe
twierdzenie.
 
{{twierdzenie|||
Każdy język kontekstowy ''''''jest językiem rekurencyjnym, czyli
<math>\displaystyle \mathcal{L}_{1}\subset \mathcal{R}.  </math>
 
}}
 
{{dowod|||
Niech  <math>\displaystyle L  </math>  będzie dowolnym językiem kontekstowym. Istnieje więc
gramatyka kontekstowa  <math>\displaystyle G=(V_{N},V_{T},v_{0},P)  </math>  taka, że  <math>\displaystyle L=L(G)  </math> . Bezpośrednio z&nbsp;definicji gramatyki
kontekstowej wynika, że słowo puste  <math>\displaystyle 1\in L  </math>  wtedy i tylko
wtedy, gdy  <math>\displaystyle v_{0}\rightarrow 1\in P  </math> . Załóżmy teraz, że dane
jest słowo  <math>\displaystyle w\in V^{*}_{T}  </math> , o którym mamy zadecydować,  czy
należy do języka  <math>\displaystyle L  </math> . W tym celu rozważmy wszystkie ciągi słów
 
<center><math>\displaystyle y_{0}=v_{0},\: y_{1},...,y_{n-1},\: y_{n}=w</math></center>
 
o tej własności, że  <math>\displaystyle y_{i}\in (V_{N}\cup V_{T})^{*}  </math> są parami różne dla  <math>\displaystyle i=0,...,n
</math> ,  <math>\displaystyle n\geq 1  </math>  oraz  <math>\displaystyle \mid y_{i}\mid \leq \mid y_{i+1}\mid  </math> .
Ilość takich ciągów jest skończona i słowo  <math>\displaystyle w\in L  </math>  wtedy i
tylko wtedy, gdy wśród powyższych ciągów znajdziemy choć jeden taki,
że tworzy wyprowadzenie w gramatyce  <math>\displaystyle G  </math> . Czyli
 
<center><math>\displaystyle y_{0}=v_{0}\rightarrow y_{1}\rightarrow ...\rightarrow
y_{n-1}\rightarrow y_{n}=w. </math></center>
 
Ponieważ ilość ciągów podlegających rozważaniom jest
skończona oraz ponieważ stwierdzenie czy pomiędzy dowolnymi słowami
zachodzi relacja  <math>\displaystyle \rightarrow  </math> , sprowadza się do przeszukania
skończonego zbioru praw  <math>\displaystyle P  </math> , efektywnie rozstrzygniemy, czy  <math>\displaystyle w
</math>  należy do języka  <math>\displaystyle L  </math>  czy też nie. }}
 
Uzyskane dotąd rezultaty możemy podsumować następująco:
 
<center><math>\displaystyle \mathcal{L}(MT)\subset \mathcal{L}_{0}\subset \mathcal{RP}</math></center>
 
W określeniu języka rekurencyjnie przeliczalnego oraz języka
rekurencyjnego wystąpiło pojęcie efektywnego algorytmu,
efektywnej procedury. Pojęcie to, intuicyjnie dość jasne, nie
zostało precyzyjnie określone. Co za tym idzie, dla
matematycznie poprawnych definicji języka rekurencyjnie
przeliczalnego i języka rekurencyjnego należałoby
sformalizować pojęcie algorytmu. W dotychczasowych rozważaniach
intuicja efektywnej procedury była o tyle wystarczająca, że
naszym celem było wskazanie istnienia takiej procedury. W
sytuacji, gdyby naszym celem było wykazanie, że taka procedura nie
istnieje, formalizacja tego pojęcia byłaby wręcz konieczna. We
wszystkich takich przypadkach powszechnie przyjmuje się, że maszyna
Turinga jest właśnie taką formalizacją. Na zdefiniowaną
w poprzednim wykładzie maszynę Turinga można spojrzeć jako na
algorytm, efektywną procedurę dającą odpowiedź pozytywną lub
negatywną w zależności od akceptacji lub nieakceptowania słowa
wejściowego. Proces obliczenia prowadzony przez maszynę Turinga
zgadza się z&nbsp;intuicyjnym rozumieniem algorytmu. O tym, że
każda efektywna procedura jest reprezentowana przez pewną
maszynę Turinga, mówi, powszechnie przyjęta jako prawdziwa, Teza
Churcha.
 
'''Teza Churcha'''
 
''Każda efektywna''
''procedura (algorytm) da się opisać przez maszynę Turinga. ''
 
Konsekwencją przyjęcia Tezy Churcha jest inkluzja  <math>\displaystyle \mathcal{RP}\subset \mathcal{L}(MT)  </math> . Biorąc pod uwagę udowodnione
powyżej twierdzenia mamy
 
<center><math>\displaystyle \mathcal{L}(MT)\subset \mathcal{L}_{0}\subset \mathcal{RP}</math></center>
 
co ostatecznie prowadzi do równości  <math>\displaystyle \mathcal{L}_{0}=\mathcal{L}(MT)  </math> .
 
==4. Rodziny <math>\displaystyle \mathcal{L}_{1}</math> i <math>\displaystyle \mathcal{L}_{0}</math> - zamkniętość na działania==
 
Dla kompletności tej części wykładu przedstawimy własności
zamkniętości rodziny języków kontekstowych  <math>\displaystyle \mathcal{L}_{1}
</math>  i języków typu '''(0)'''  <math>\displaystyle \mathcal{L}_{0}  </math>  ze względu na
najczęściej używane operacje. W niektórych przypadkach dowody
dotyczące obu klas są takie same. W dowodach posłużymy się specjalną
postacią gramatyk, a mianowicie taką, w której symbole terminalne
występują tylko po prawej stronie. Prawdziwe bowiem jest twierdzenie
 
{{twierdzenie|||
Dla każdej gramatyki istnieje równoważna gramatyka tego
samego typu taka, że każda produkcja, w której występuje symbol
terminalny  <math>\displaystyle a  </math> , jest postaci  <math>\displaystyle v\longrightarrow a  </math> .
 
}}
 
Elementarny dowód tej własności pozostawiamy jako zadanie
domowe.
 
{{twierdzenie|||
Rodziny  <math>\displaystyle \mathcal{L}_{0}  </math>  i  <math>\displaystyle \mathcal{L}_{1}  </math>  są zamknięte
ze względu na
#  sumę mnogościową,
#  iloczyn mnogościowy,
#  katenację,
#  iterację `{} <math>\displaystyle *  </math> `{},
#  odbicie zwierciadlane.
 
}}
 
{{dowod|||
{}
 
[[##Lsuma|Uzupelnic Lsuma|]]. Niech dla  <math>\displaystyle i=1,2  \displaystyle G_{i}=(V_{N}^{i},V_{T},v_{0}^{i},P_{i})  </math>  będą gramatykami
typu  <math>\displaystyle (1)  </math>  (odpowiednio typu  <math>\displaystyle (0)  </math> ), takimi, że  <math>\displaystyle V_{N}^{1}\cap V_{N}^{2}=\emptyset  </math> . I niech  <math>\displaystyle L_{i}=L(G_{i})  </math> .
Określamy gramatykę  <math>\displaystyle G  </math>  typu  <math>\displaystyle (1)  </math>  (typu  <math>\displaystyle (0)  </math> )
generującą język  <math>\displaystyle L_{1}\cup L_{2}  </math> .
 
Jeśli  <math>\displaystyle 1\notin L_{1}\cup L_{2}  </math> , to przyjmujemy
 
<center><math>\displaystyle G=(V_{N}^{1}\cup V_{N}^{2}\cup \left\{ v_{0}\right\}
,V_{T},v_{0},P_{1}\cup P_{2}\cup \left\{ v_{0}\rightarrow
v_{0}^{1},\, v_{0}\rightarrow v_{0}^{2}\right\} ).</math></center>
 
Zauważmy, że wówczas w żadnej z gramatyk nie ma prawa wymazującego.
Jeśli natomiast  <math>\displaystyle 1\in L_{1}\cup L_{2}  </math> , to konstruujemy
gramatykę  <math>\displaystyle G  </math>  dla języków  <math>\displaystyle L_{1}\setminus \left\{ 1\right\}
</math>  i  <math>\displaystyle L_{2}\setminus \left\{ 1\right\}  </math> , jak powyżej, a
następnie dodajemy nowy symbol początkowy  <math>\displaystyle \overline{v}_{0}  </math>  i
prawa  <math>\displaystyle \overline{v}_{0}\rightarrow v_{0},\,
\overline{v}_{0}\rightarrow 1  </math> .
 
[[##Liloczyn|Uzupelnic Liloczyn|]]. Przecięcie udowodnimy tylko dla języków typu  <math>\displaystyle (0)
</math> . Dowód dla języków kontekstowych został przeprowadzony
wcześniej.
 
Niech dla  <math>\displaystyle i=1,2  \displaystyle G_{i}=(V_{N}^{i},V_{T},v_{0}^{i},P_{i})  </math>
będą gramatykami typu  <math>\displaystyle (0)  </math> , takimi, że  <math>\displaystyle V_{N}^{1}\cap
V_{N}^{2}=\emptyset  </math> . Niech  <math>\displaystyle L_{i}=L(G_{i})  </math> . Określamy
gramatykę  <math>\displaystyle G  </math>  typu  <math>\displaystyle (0)  </math>  generującą język  <math>\displaystyle L_{1}\cap L_{2}
</math>  przyjmując
 
<center><math>\displaystyle G=(V_{N}^{1}\cup V_{N}^{2}\cup V_{N},V_{T},v_{0},P_{1}\cup P_{2}\cup
P),</math></center>
 
gdzie:  <math>\displaystyle V_{N}=\left\{ v_{a}\, :\, a\in V_{T}\right\} \cup \left\{ v_{0},\overline{v}_{1},\overline{v}_{2}\right\}  </math> ,
a do zbioru  <math>\displaystyle P  </math>  należą prawa <br>
(1)  <math>\displaystyle v_{0}\rightarrow \overline{v}_{1}v_{0}^{1}\overline{v}_{2}v_{0}^{2}\overline{v}_{1}  </math> <br>
(2)  <math>\displaystyle \overline{v}_{2}a\rightarrow v_{a}\overline{v}_{2}  </math>  dla  <math>\displaystyle \forall a\in V_{T}  </math> <br>
(3)  <math>\displaystyle bv_{a}\rightarrow v_{a}b  </math>  dla  <math>\displaystyle \forall a,b\in V_{T}  </math> <br>
(4)  <math>\displaystyle \overline{v}_{1}v_{a}a\rightarrow a\overline{v}_{1}  </math>  dla  <math>\displaystyle \forall a\in V_{T}  </math> <br>
(5)  <math>\displaystyle \overline{v}_{1}\overline{v}_{2}\overline{v}_{1}\rightarrow 1  </math> <br>
Przy pomocy prawa (1) i wszystkich praw ze zbioru  <math>\displaystyle P_{1}\cup P_{2}
</math>  można wygenerować zbiór słów
 
<center><math>\displaystyle \left\{ \overline{v}_{1}w_{1}\overline{v}_{2}w_{2}\overline{v}_{1}\,
:\, w_{1}\in L_{1},\, w_{2}\in L_{2}\right\} .</math></center>
 
Z dowolnego słowa należącego do tego zbioru, korzystając
z praw (2)-(4) można wyprowadzić słowo  <math>\displaystyle w_{1}\overline{v}_{1}\overline{v}_{2}\overline{v}_{1}  </math>  wtedy i
tylko wtedy, gdy  <math>\displaystyle w_{1}=w_{2}  </math> . Korzystając z prawa (5)
dostajemy słowo  <math>\displaystyle w_{1}  </math> . A więc  <math>\displaystyle L(\overline{G})=L_{1}\cap
L_{2}  </math> .
 
[[##Lkatenacja|Uzupelnic Lkatenacja|]]. Niech dla  <math>\displaystyle i=1,2  \displaystyle G_{i}=(V_{N}^{i},V_{T},v_{0}^{i},P_{i})  </math>  będą tak jak poprzednio
gramatykami typu  <math>\displaystyle (1)  </math>  ( <math>\displaystyle (0)  </math> ) takimi, że  <math>\displaystyle V_{N}^{1}\cap V_{N}^{2}=\emptyset  </math>  oraz spełniającymi warunki
powyższego twierdzenia. Niech  <math>\displaystyle L_{i}=L(G_{i})  </math> . Określamy
gramatykę  <math>\displaystyle G  </math>  odpowiednio typu  <math>\displaystyle (1)  </math>  ( <math>\displaystyle (0)  </math> ) generującą
język  <math>\displaystyle L_{1}L_{2}  </math> .
 
Jeśli  <math>\displaystyle 1\notin L_{1}\cup L_{2}  </math> , to przyjmujemy
 
<center><math>\displaystyle G=(V_{N}^{1}\cup V_{N}^{2}\cup \left\{ v_{0}\right\}
,V_{T},v_{0},P_{1}\cup P_{2}\cup \left\{ v_{0}\rightarrow
v_{0}^{1}v_{0}^{2}\right\} ).</math></center>
 
Jeśli  <math>\displaystyle 1\in L_{1}\cup L_{2}  </math> , to oznaczamy  <math>\displaystyle L_{1}'=L_{1}\setminus \left\{ 1\right\} ,\; \; L_{2}'=L_{2}\setminus
\left\{ 1\right\}  </math> . Wówczas
 
<center><math>\displaystyle L_{1}L_{2}=\left\{ \begin{array} {lll}
L_{1}'L_{2}'\cup L_{1}' & gdy & 1\in L_{2},\: 1\notin L_{1}\\
L_{1}'L_{2}'\cup L_{2}' & gdy & 1\in L_{1},\: 1\notin L_{2}\\
L_{1}'L_{2}'\cup L_{1}'\cup L_{2}'\cup \left\{ 1\right\}  & gdy &
1\in L_{1},\: 1\in L_{2}
\end{array} \right. </math></center>
 
Wykorzystując poprzednią konstrukcję i zamkniętość
ze względu na sumę w każdym z tych przypadków otrzymujemy gramatykę
generującą katenację języków  <math>\displaystyle L_{1}  </math>  i  <math>\displaystyle L_{2}  </math> .
 
[[##Literacja|Uzupelnic Literacja|]]. Niech  <math>\displaystyle G=(V_{N},V_{T},v_{0},P)  </math>  będzie
gramatyką typu  <math>\displaystyle (1)  </math>  (typu  <math>\displaystyle (0)  </math> ) taką, że symbole
terminalne nie występują po lewej stronie żadnej produkcji z  <math>\displaystyle P
</math> . Załóżmy też, że  <math>\displaystyle 1\notin L=L(G)  </math> . Gramatyka
 
<center><math>\displaystyle \overline{G}=(V_{N}\cup \left\{
\overline{v}_{0},\overline{v}_{1}\right\}
,V_{T},\overline{v}_{0},\overline{P}),</math></center>
 
gdzie
 
<center><math>\displaystyle \overline{P}=P\cup \begin{array} [t]{l}
\left\{ \overline{v}_{0}\rightarrow 1,\: \overline{v}_{0}\rightarrow v_{0},\: \overline{v}_{0}\rightarrow \overline{v}_{1}v_{0}\right\} \cup \\
\left\{ \overline{v}_{1}a\rightarrow \overline{v}_{1}v_{0}a\, :\, a\in V_{T}\right\} \cup \\
\left\{ \overline{v}_{1}a\rightarrow v_{0}a\, :\, a\in V_{T}\right\}
\end{array} </math></center>
 
generuje język  <math>\displaystyle L^{*}  </math> . Jeśli  <math>\displaystyle 1\in L  </math> , to usuwamy prawo
wymazujące  <math>\displaystyle v_{0}\rightarrow 1  </math>  i dla języka  <math>\displaystyle L\setminus
\left\{ 1\right\}  </math>  konstruujemy gramatykę  <math>\displaystyle \overline{G}  </math> . Z
faktu, że  <math>\displaystyle (L\cup \left\{ 1\right\} )^{*}=L^{*}  </math> , wynika, że
również  <math>\displaystyle L(\overline{G})=L^{*}  </math> .
 
[[##Lodbicie|Uzupelnic Lodbicie|]]. Jeśli  <math>\displaystyle G=(V_{N},V_{T},v_{0},P)  </math>  jest gramatyką
typu  <math>\displaystyle (1)  </math>  (typu  <math>\displaystyle (0)  </math> ) taką, że  <math>\displaystyle L(G)=L  </math> , to gramatyka
<math>\displaystyle \overleftarrow{G}=(V_{N},V_{T},v_{0},\overleftarrow{P})  </math> , gdzie
<math>\displaystyle \overleftarrow{P}=\left\{ \overleftarrow{x}\rightarrow
\overleftarrow{y}\, :\, x\rightarrow y\in P\right\}  </math>  generuje
język  <math>\displaystyle \overleftarrow{L}  </math> . }}
 
Zauważmy na koniec, że rodzina  <math>\displaystyle \mathcal{L}_{0}  </math>  nie jest
zamknięta ze względu na uzupełnienie.
Stwierdzenie to
wynika z przyjęcia jako obowiązujacej Tezy Churcha, która w tym
wypadku implikuje równość rodziny języków  <math>\displaystyle \mathcal{L}_{0}  </math>  i
rodziny języków rekurencyjnie przeliczalnych oraz z faktu, iż
istnieje język rekurencyjnie przeliczalny, którego
uzupełnienie nie jest rekurencyjnie przeliczalne. Ten ostatni fakt
podajemy bez dowodu.
Dodajmy, że własność zamkniętości ze względu na uzupełnienie dla
rodziny  <math>\displaystyle \mathcal{L}_{1}  </math>  przez długi czas pozostawała problemem otwartym.
Dopiero w roku 1987 udowodniono, iż własność ta jest spełniona  dla
języków kontekstowych.
Podsumowanie własności zamkniętości ze względu na działania dla
różnych klas języków hierarchii Chomsky'ego zawarte jest w poniższej
tabeli.
 
{0.3cm} {
 
{| border=1
|+ <span style="font-variant:small-caps">Uzupelnij tytul</span>
|-
|  ||  3 ||
2 ||  1 ||
0
|-
|    <math>\displaystyle \cup  </math>  ||  T ||  T ||  T ||
T
|-
|  <math>\displaystyle \cdot  </math>  ||  T ||  T ||  T ||
T
|-
|  <math>\displaystyle \star  </math>  ||  T ||  T ||  T ||
T
|-
|  <math>\displaystyle \setminus  </math>  ||  T ||  N ||  T ||
N
|-
|  <math>\displaystyle \cap  </math>  ||  T ||  N ||  T ||
T
|-
|
 
|}
 
}
{0.3cm}
 
Na koniec podamy
twierdzenie o  wzajemnych relacjach pomiędzy
rodzinami języków z hierarchii Chomsky'ego. Dowód tego twierdzenia
wynika w&nbsp;części  z definicji typów gramatyk wprowadzonych na wykładzie 2, a w części
z udowodnionych własności poszczególnych rodzin języków z
hierarchii Chomsky'ego (zakładając obowiązywanie Tezy Churcha).
 
{{twierdzenie|||
Rodziny języków  hierarchii Chomsky'ego spełniają następujące relacje
 
<center><math>\displaystyle \mathcal{L}_{0}\subseteq \! \! \! \! \! \! _{/}\, \mathcal{L}_{1}
\subseteq \! \! \! \! \! \! _{/}\, \mathcal{L}_{2}
\subseteq \! \! \! \! \! \! _{/}\, \mathcal{L}_{3}
</math></center>
 
.
 
}}
 
==5. Problemy rozstrzygalne==
 
W poprzednim wykładzie uzasadniliśmy, że dla każdej
deterministycznej maszyny Turinga jesteśmy w stanie wskazać taką
która akceptuje dany język i jednocześnie zatrzymuje się tylko na
słowach akceptowanych. Wymagało to przejścia przez maszynę
niedeterministyczną a następnie jej symulację na maszynie
deterministycznej. Z tego powodu ograniczamy się w dalszej części
wykładu tylko do tego typu maszyn Turinga (akceptacja<nowiki>=</nowiki>stop). Jak to
uzasadniono wcześniej, przy założeniu Tezy Churcha, maszyna Turinga
może być rozpatrywana jako matematycznie ścisła definicja algorytmu.
 
Pojęcie rozstrzygalnego problemu zostało wprowadzone wcześniej, na innym
wykładzie i jest ono znane.
Przypomnijmy więc tylko,
że rozstrzygalność czy też
nierozstrzygalność odnosi się do pewnej klasy, którą tworzą
określone przypadki ustalonego problemu. Jeśli istnieje algorytm,
który rozwiązuje taki problem dla wszystkich przypadków w tej
klasy, to mówimy, że problem jest rozstrzygalny (w tej klasie). Zatem
taki algorytm jest uniwersalnym sposobem rozwiązywania problemu dla
wszystkich danych wejściowych określających poszczególne przypadki w tej
klasie. Jak łatwo zauważyć dla ustalenia rozstrzygalności problemu wystarczy się
opierać na intuicyjnym pojęciu algorytmu. Są jednak
takie problemy, dla których nie istnieje, w rozważanej klasie
przypadków, uniwersalny sposób ich rozwiazywania. Takie problemy
nazywamy nierozstrzygalnymi w danej klasie. Aby wykazać
nierozstrzygalność jakiegoś problemu, nieodzownym jest sformalizowanie pojęcia
algorytmu.
Standardowo taką formalizacją jest, o czym
wspomniano już wcześniej, maszyna Turinga.
 
Zwróćmy uwagę, że maszyna Turinga akceptuje języki, gdy tym czasem
przyzwyczajeni jesteśmy, że algorytmy (programy) rozwiązują pewne,
niekiedy bardzo skomplikowane problemy (określone przy pomocy list,
kolejek, grafów itp.). Zwracamy zatem uwagę na fakt, że w przypadku
maszyny Turinga musimy wykonać wstępne umowne kodowanie naszego
problemu. W tym przypadku rozważany język określa te spośród
"sensownych" kodowań, które stanowią rozwiązanie postawionego
problemu. Z drugiej strony maszyna akceptując słowo <math>\displaystyle \sharp w_1 \$
w_2 \sharp</math> może informować nas o tym, że wynikiem obliczeń
numerycznych na danych zakodowanych w <math>\displaystyle w_1</math> rzeczywiście jest liczba
zakodowana w <math>\displaystyle w_2</math> itp.
 
Dla ilustracji powyższych dywagacji rozważmy problem skończoności
w&nbsp;klasie jezyków regularnych. Problem ten jest rozstrzygalny, bo w
oparciu o lemat o&nbsp;pompowaniu można skonstruować algorytm,
który dla dowolnego języka regularnego rozstrzygnie,
czyli odpowie twierdząco lub przecząco na pytanie o jego
skończoność. W tym przypadku można np. przyjąć, że jako słowo
wejściowe podajemy zakodowany opis gramatyki generującej język.
 
Nierozstrzygalność algorytmiczna problemu w ustalonej klasie
nie oznacza, podkreślmy, niemożliwości rozwiazania konkretnego zadania z tej
klasy. Nierostrzygalność oznacza niemożliwość rozwiązania za pomocą
tego samego algorytmu, tej samej metody, wszystkich przypadków tego
problemu należących do danej klasy.
 
W zamieszczonej poniżej tabeli przedstawiamy najczęściej rozważane pod kątem
rozstrzygalności  problemy z dziedziny
języków formalnych w ramach hierarchii Chomsky'ego.
Litera R oznacza rozstrzygalność problemu, N
nierostrzygalność, a znak - pojawiający się przy problemie
jednoznaczności oznacza, że problemu tego nie formułuje się dla
gramatyk kontekstowych i typu '''(0)'''.
 
{0.3cm} {
 
{| border=1
|+ <span style="font-variant:small-caps">Uzupelnij tytul</span>
|-
|
własność ||  '''(3)''' ||  '''(2)''' ||  '''(1)''' ||
'''(0)'''
|-
|  należenie  <math>\displaystyle w\in L  </math>  ||  R ||  R ||  R ||
N
|-
|  inkluzja  <math>\displaystyle L_{1}\subset L_{2}  </math>  ||  R ||  N ||  N ||
N
|-
|  równoważność ||  R ||  N ||  N ||
N
|-
|  pustość  <math>\displaystyle L=\emptyset  </math>  ||  R ||  R ||  N ||
N
|-
|  nieskończoność  <math>\displaystyle card\, L=\aleph _{0}  </math>  ||  R ||  R ||  N ||
N
|-
|  jednoznaczność gramatyki ||  R ||  N ||  - ||
-
|-
|
 
|}
 
}
{0.3cm}
 
Najczęściej używaną metodą dowodzenia nierozstrzygalności problemu
<math>\displaystyle P  </math>  jest redukcja tego problemu do innego, powiedzmy  <math>\displaystyle P'
</math> , dla którego nierozstrzygalność została ustalona wcześniej.
Redukcja taka prowadzi do sformułowania implikacji:
 
jeśli  <math>\displaystyle P  </math>  byłby
rozstrzygalny, to i  <math>\displaystyle P'  </math>  byłby rozstrzygalny.
 
A ponieważ to
ostatnie (następnik implikacji) nie jest prawdą, więc problem  <math>\displaystyle P
</math>  nie jest rozstrzygalny.
 
Należy w tym miejscu podkreślić fakt, że dowody nierozstrzygalności
problemów uniwersalnych (takich jak problem Posta rozważany dalej)
wiążą się z konstrukcją odpowiednich maszyn Turinga, kodowaniem
problemu, a następnie dowodem uzasadniającym, że problem jest
rzeczywiście nierozstrzygalny. Tematyka ta
wykracza  poza ramy wykładu. Z tego też powodu ograniczymy się  tutaj
do zaprezentowania jednego ze znanych problemów nierozstrzygalnych
bez dowodu nierozstrzygalności.
 
Najczęściej występującym w literaturze problemem nierozstrzygalnym jest, bez wątpienia,
problem
Posta przedstawiony poniżej.
 
&nbsp;
 
'''Problem''' '''Posta'''
 
Dla dowolnego alfabetu  <math>\displaystyle A  </math> , o co najmniej dwóch elementach ( <math>\displaystyle \sharp A\geq 2  </math> ), załóżmy, iż dana jest, tak zwana, lista słów, a dokładniej
par słów ''' <math>\displaystyle \left( u_{1},w_{1}\right) \: \left(
u_{2},w_{2}\right) \: \ldots \left( u_{n},w_{n}\right),    </math> ''' gdzie
<math>\displaystyle u_{i},w_{i}\in A^{+}  </math> ,  <math>\displaystyle n\in \Bbb N  </math> . Mówimy, że taka lista ma własność
Posta (problem Posta ma rozwiązanie), jeśli istnieje ciąg indeksów
<math>\displaystyle i_{1},\ldots ,i_{k}\in \{1,...,n\}  </math>  taki, że
 
<center><math>\displaystyle u_{i_{1}}\ldots u_{i_{k}}=w_{i_{1}}\ldots w_{i_{k}}.</math></center>
 
Jest to w ogólnym przypadku problem nierozstrzygalny.
 
Problem ten można sformułować równoważnie następująco. Niech  <math>\displaystyle A  </math>  będzie
alfabetem interpretowanym jako zbiór indeksów, a  <math>\displaystyle B
</math>  dowolnym alfabetem. Niech  <math>\displaystyle h:A^{*}\longrightarrow
B^{*},\: g:A^{*}\longrightarrow B^{*}  </math> będą dowolnymi
homomorfizmami. Problem Posta, inaczej sformułowany, polega na odpowiedzi
na pytanie, czy istnieje słowo  <math>\displaystyle x\in A^{+}  </math>  takie, że  <math>\displaystyle h(x)=g(x)  </math> .
 
Dwa kolejne przykłady ilustrują technikę redukcji pewnych
problemów do problemu Posta. W efekcie
uzyskujemy nierozstrzygalność w sposób opisany powyżej.
 
{{twierdzenie|||
W klasie gramatyk bezkontekstowych problem
niejednoznaczności jest nierozstrzygalny.
 
}}
 
{{dowod|||
Udowodnimy, że problem jest nierozstrzygalny dla gramatyk
bezkontekstowych generujących jązyki nad alfabetem
dwuelementowym  <math>\displaystyle A=\{a,b\}  </math> . Oznaczmy  <math>\displaystyle B=\{d,e\}  </math>  i
określmy homomorfizm  <math>\displaystyle h:B^{*}\longrightarrow A^{*}  </math> ,
przyjmując  <math>\displaystyle h(d)=ba^{2}  </math>  oraz  <math>\displaystyle h(e)=ba^{3}  </math> . Niech  <math>\displaystyle u  </math>
będzie ciągiem  <math>\displaystyle u_{1},...,u_{n}\in B^{+}  </math> dowolnie wybranych
i&nbsp;ustalonych słów. Dla dowolnej liczby naturalnej  <math>\displaystyle i>0  </math>  niech  <math>\displaystyle \overline{i}=de^{i}  </math> . Określony poniżej język
 
<center><math>\displaystyle L_{u}=\{h(\overline{i_{1}}).....h(\overline{i_{k}})bah(u_{i_{k}}),...,h(u_{i_{1}})\in
A^{*}\: :\: k\geq 1,\: 1\leq i_{j}\leq n\}</math></center>
 
jest językiem
bezkontekstowym, jako generowany  przez gramatykę  <math>\displaystyle G_{u}=(V_{N},V_{T},v_{0},P_{u})  </math> , dla której
 
<math>\displaystyle V_{N}=\{v_{u}\},\: V_{T}=\{a,b\},\: v_{0}=v_{u}  </math>  oraz  <math>\displaystyle P_{x}=\{v_{u}\rightarrow h(\overline{i})v_{u}h(u_{i}),\:
v_{u}\rightarrow h(\overline{i})bah(u_{i})\}  </math> .
 
Niech teraz  <math>\displaystyle u  </math>  i  <math>\displaystyle w  </math>  oznaczają ciągi dowolnie wybranych i
ustalonych słów  <math>\displaystyle u_{1},...,u_{n}\in B^{+}  </math>  i  <math>\displaystyle w_{1},...,w_{n}\in B^{+}  </math> . Tworzą one listę słów ''' <math>\displaystyle \left( u_{1},w_{1}\right) \: \left( u_{2},w_{2}\right) \: \ldots
\left( u_{n},w_{n}\right),  </math> '''. Zatem  zasadne jest  postawienie
pytania, czy lista ta ma własność Posta. Niech  <math>\displaystyle G_{u}  </math>  oraz  <math>\displaystyle G_{w}
</math>  będą gramatykami bezkontekstowymi określonymi tak jak
powyżej. Gramatyka  <math>\displaystyle G=(\{v_{0},v_{u},v_{w}\},\{a,b\},v_{0},P)  </math> ,
gdzie  <math>\displaystyle P=\{v_{0}\rightarrow v_{u},\: v_{0}\rightarrow v_{w}\}\cup
P_{u}\cup P_{w}  </math>  jest bezkontekstowa. Gramatyka ta jest
niejednoznaczna wtedy i tylko wtedy, gdy  <math>\displaystyle L_{u}\cap L_{y}\neq
\emptyset  </math> . Ten ostatni warunek równoważny jest istnieniu liczb
<math>\displaystyle i_{1},...,i_{k}\in \Bbb N  </math>  takich, że  <math>\displaystyle u_{i_{1}}.....u_{i_{k}}=w_{i_{1}}.....w_{i_{k}}  </math>  , czyli własności
Posta listy ''' <math>\displaystyle \left( u_{1},w_{1}\right) \: \left(
u_{2},w_{2}\right) \: \ldots \left( u_{n},w_{n}\right)  </math> .'''Ostatecznie więc
rozstrzygalność problemu niejednoznaczności w klasie
gramatyk bezkontekstowych prowadziłaby do
rozstrzygalności własności Posta. }}
 
Dla drugiego przykładu przyjmijmy jako alfabety  <math>\displaystyle A_{2}=\left\{ a,b\right\} ,\: A_{3}=\left\{ a,b,c\right\}  </math>  oraz określmy
język
 
<center><math>\displaystyle L=\left\{ v_{1}cv_{2}c\overleftarrow{v}_{2}c\overleftarrow{v}_{1}\,
:\, v_{1},v_{2}\in A_{2}^{*}\right\}. </math></center>
 
Ustalmy listę Posta ''' <math>\displaystyle \left( u_{1},w_{1}\right) \: \left( u_{2},w_{2}\right) \: \ldots \left( u_{n},w_{n}\right)  </math> '''
nad alfabetem  <math>\displaystyle A_{2}  </math> , gdzie  <math>\displaystyle u_{i},w_{i}\in A_{2}^{+}
</math> . Wprowadzamy  teraz języki  <math>\displaystyle L_{u},\: L_{w}\: L_{PP}  </math>  nad
alfabetem  <math>\displaystyle A_{3}  </math>  przyjmując:
 
<center><math>\displaystyle \aligned L_{u} &= \left\{ ba^{i_{k}}b\ldots ba^{i_{1}}cu_{i_{1}}\ldots u_{i_{k}}\, :\, k\geq 1,1\leq i_{j}\leq n\right\} \\
L_{w} &= \left\{ ba^{i_{k}}b\ldots ba^{i_{1}}cw_{i_{1}}\ldots
w_{i_{k}}\, :\, k\geq 1,1\leq i_{j}\leq n\right\}
\endaligned</math></center>
 
oraz definiujemy język
 
<center><math>\displaystyle L_{PP}=L_{u}c\overleftarrow{L}_{w}.</math></center>
 
Określone powyżej języki nad alfabetem  <math>\displaystyle A_{3}  </math>  mają
własności konieczne do zastosowania  lematu, który przytoczymy bez
dowodu.
 
{{lemat|||
Języki  <math>\displaystyle L,\: L_{PP},\: A_{3}^{*}\setminus L,\: A_{3}^{*}\setminus
L_{PP}  </math>  są bezkontekstowe.
 
}}
 
Dla języków  <math>\displaystyle L  </math>  i  <math>\displaystyle L_{PP}  </math>  uzasadnienie ich bezkontekstowości jest proste
poprzezkonstrukcję odpowiednich
gramatyk. Aby uzyskać bezkontekstowość ich uzupełnień,
należy
podzielić rozważane języki na rozłączne podzbiory i konstruować
gramatyki bezkontekstowe dla tych wyróżnionych
podzbiorów, a w końcu wykorzystać fakt, że suma języków
bezkontekstowych jest językiem bezkontekstowym.
 
Zauważmy teraz, że istnienie rozwiązania problemu Posta nad
alfabetem  <math>\displaystyle A_{3}  </math>  jest równoważne temu, że  <math>\displaystyle L_{PP}\cap
L\neq \emptyset  </math> .


Jeśli bowiem  <math>\displaystyle L_{PP}\cap L\ni ba^{i_{k}}\ldots
W ten sposób zawsze przeprowadzamy konfigurację <math>\sharp s_0 w \sharp
ba^{i_{1}}cu_{i_{1}}\ldots u_{i_{k}}c\overleftarrow{w_{i_{1}}\ldots
</math> na konfigurację <math>\sharp s_1 w' \sharp</math>, przy czym <math>w'=1^i 2^j 3^k</math>
w_{i_{k}}}ca^{i_{1}}b\ldots a^{i_{k}}b  </math> , gdzie  <math>\displaystyle k\geq 1,1\leq
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>.
i_{j}\leq n  </math> , to oczywiście  <math>\displaystyle u_{i_{1}}\ldots u_{i_{k}}=w_{i_{1}}\ldots
w_{i_{k}}  </math> . Jeśli ciąg  <math>\displaystyle i_{1},\ldots ,i_{k} </math> jest
rozwiązaniem problemu Posta, to  <math>\displaystyle (i_{1},\ldots
,i_{k})(i_{1},\ldots ,i_{k})  </math> też. Zatem jeśli  <math>\displaystyle L_{PP}\cap L\neq
\emptyset  </math> , to język  <math>\displaystyle L_{PP}\cap L  </math> jest nieskończony.


Wobec nierozstrzygalności problemu Posta wnioskujemy, że
Warunek <math>L_1 \propto L_2</math> otrzymujemy w sposób identyczny. Trzeba
nierozstrzygalny jest problem pustości i problem nieskończoności
tylko wypisać odpowiednią ilość symboli <math>4</math> (a wiemy już, jak
przecięcia  <math>\displaystyle L_{1}\cap L_{2}  </math> w klasie języków
konstruować liczbę <math>2n</math>, mając dane <math>n</math>).
bezkontekstowych.

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).