Teoria Obliczeń i Złożoności Obliczeniowej
Transkrypt
Teoria Obliczeń i Złożoności Obliczeniowej
Teoria Obliczeń i Złożoności Obliczeniowej Laboratorium: Maszyna Turinga. Zapoznanie się z modelem obliczeń w postaci jednotaśmowej Maszyny Turinga. Maszyna Turinga to stworzony przez Alana Turinga abstrakcyjny model komputera służący do wykonywania algorytmów. Maszyna Turinga składa się z nieskończenie długiej taśmy podzielonej na pola. Taśma może być nieskończona jednostronnie lub obustronnie. Każde pole może znajdować się w jednym z N stanów. Maszyna zawsze jest ustawiona nad jednym z pól i znajduje się w jednym z M stanów. Zależnie od kombinacji stanu maszyny i pola maszyna zapisuje nową wartość w polu, zmienia stan i przesuwa się o jedno pole w prawo, w lewo lub pozostaje na miejscu. Liczby N i M mogą być dowolne, byle skończone. Wyróżnia się też stan (M+1)-szy, który oznacza zakończenie pracy maszyny. Maszyny Turinga są interesujące z kilku względów: udowodniono, że zwykłe komputery są równoważne maszynie Turinga, jest wygodnym narzędziem przy precyzowaniu pojęć i problemów procesu poznawania i sztucznej inteligencji. Jeżeli dla każdej konfiguracji Maszyny Turinga dopuszczalne jest wykonanie co najwyżej 1 instrukcji, o taką maszynę nazywamy deterministyczną (DTM). Złożoność czasowa T(n) DTM równa jest sumie ruchów głowicy maszyny wykonywanych podczas przetwarzania słowa wejściowego o długości n (bierzemy pod uwagę zawsze najgorszy przypadek). Złożoność pamięciową S(n) DTM obliczamy jako odległość od lewego końca taśmy jaką może osiągnąć głowica maszyny w trakcie przetwarzania słowa wejściowego o długości n (w najgorszym przypadku). Analiza algorytmu dla DTM Przeanalizujemy program dodający do siebie dwie liczby w zapisie binarnym. Do analizy programu posłużymy się symulatorem Maszyny Turinga. Symulator dane na temat wszystkich możliwych stanów automatu przechowuje w pliku tekstowym. Oto jak wygląda zapis algorytmu dodawania w formacie akceptowanym przez program: 0|0 1|1 2|3 3|3 4|4 5|5 6| 0 Z 0 b 0 0 0 1|0 1|1 -1|4 -1|3 -1|4 -1|5 | 1 J 1 b 1 1 1 1|1 1|1 -1|5 -1|3 -1|4 -1| | x x x b x x 1| 1|1 -1| -1|1 -1|6 |5 |6 J J 1 0 1 Z | 1|1 | 1|1 -1|1 -1|5 -1|1 Z Z 0 1 0 J | 1|2 | 1|1 1|1 -1|7 1|1 b | b -1| | 0 1| 1 1| b 1| J 1| Tabelka zawiera w pierwszej kolumnie numery aktualnych stanów maszyny, w pierwszym wierszu jest zapisany aktualny symbol na taśmie. Na przecięciu się kolumn i wierszy znajdują się kolejno: numer kolejnego stanu maszyny, symbol na który zostanie wymieniony symbol bieżący oraz kierunek ruchu głowicy maszyny (1 – prawo, -1 – lewo). Ten sam algorytm można przedstawić w postaci grafu przejść między stanami Maszyny Turinga: Algorytm przyjmuje jako dane wejściowe dwie liczby binarne oddzielone przez 'x', np. 101x011. Przeprowadzimy test dla dodawania 2 liczb: 5+3. Oczekujemy wyniku 8, czyli binarnie 1000. Kolejne przejścia Maszyny Turinga doprowadziły właśnie do takiego wyniku: _[1]_01x011 Stan: 0 J_[0]_1x011 Stan: 0 JZ_[1]_x011 Stan: 0 JZJ_[x]_011 Stan: 0 JZJx_[0]_11 Stan: 1 . . . _[b]_1000bb Stan: 5 b_[1]_000bb Stan: 7 Wynik na taśmie: b1000bbbbb Łatwo wyznaczyć złożoność pamięciową tego programu. Jeżeli założymy, że dodajemy 2 liczby n-bitowe to S(n)=2n+1+1 (1 znak przeznaczony jest na separator 'x', a drugi na zaznaczenie końca, czyli symbol 'b'). Program wykorzystuje więc za każdym razem 2n+2 pól na taśmie. Oznacza to, że złożoność pamięciowa zależy liniowo od wielkości wprowadzonej liczby binarnej. Spróbujmy wyznaczyć złożoność czasową. Program najpierw czyta 2 liczby n-bitowe. Następnie przeprowadzane jest dodawanie – bit po bicie odbywa się kopiowanie bitów liczby 2 do pierwszej, a następnie wykonywane jest dodawanie na bitach. Oznacza to n-krotne przesunięcie głowicy o średnio n pól tam i z powrotem. N-krotne kopiowanie wszystkich bitów liczby długości n sprawia, że algorytm charakteryzuje się kwadratową złożonością czasową.