Języki, automaty i obliczenia/Wykład 13: Złożoność obliczeniowa.

Z Studia Informatyczne
Wersja z dnia 21:37, 11 wrz 2023 autorstwa Luki (dyskusja | edycje) (Zastępowanie tekstu – „.↵</math>” na „</math>”)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)
Przejdź do nawigacjiPrzejdź do wyszukiwania

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