Raport z projektu
Transkrypt
Raport z projektu
Raport z projektu Przedmiot: Algorytmy i struktury danych 1 Projekt: Wieża Hanoi Autor: Wojciech Topolski Problem wieży Hanoi “W wielkiej świątyni Benares w Hanoi, pod kopułą, która zaznacza środek świata, znajduje się płytka z brązu, na której umocowane są trzy diamentowe igły, wysokie na łokieć i cienkie jak talia osy. Na jednej z tych igieł, w momencie stworzenia świata, Bóg umieścił 64 krążki ze szczerego złota. Największy z nich leży na płytce z brązu, a pozostałe jeden na drugim, idąc malejąco od największego do najmniejszego. Jest to wieża Brahma. Bez przerwy we dnie i w nocy kapłani przekładają krążki z jednej diamentowej igły na drugą, przestrzegając niewzruszonych praw Brahma. Prawa te chcą, aby kapłan na służbie brał tylko jeden krążek na raz i aby umieszczał go na jednej z igieł w ten sposób, by nigdy nie znalazł się pod nim krążek mniejszy. Wówczas, gdy 64 krążki zostaną przełożone z igły, na której umieścił je Bóg w momencie stworzenia świata, na jedną z dwóch pozostałych igieł, wieża, świątynia, bramini rozsypią się w proch i w jednym oka mgnieniu nastąpi koniec świata.” Dla 3 krążków problem można rozwiązać stosując prosty algorytm rekurencyjny, lub iteracyjny. Algorytm rekurencyjny jest postaci: void Hanoi(int n, int d) { if (n == 0) return; Hanoi(n – 1, -d); Przenies(n, d); Hanoi(n – 1, -d); } // Przenoszenie n-tego krazka od d pozycji Można jednak rozwiązać ten problem za pomocą algorytmu iteracyjnego. Aby to osiągnąć należy zauważyć, że krążki są przemieszczane sposób ustalony za pomocą następującego wzoru: np. n-ty krążek zaczyna z pozycji 1 słupka i jest przenoszony co 2 n−2n−1 o x=−1n1 , gdzie jeśli x = 1 to przenosimy krążek na słupek po prawej stronie, natomiast jeśli x = -1 to przenosimy krążek na słupek po lewej stronie. Możemy to rozpisać: Krążek nr. 1 przemieszcza się co 2^1 przeniesienie { 1, 3, 5, 7, 9, ..., 2^k – 2^(1 – 1) } zaczynając od 2^0 Krążek nr. 2 przemieszcza się co 2^2 przeniesienie { 2, 6, 10, 14, ..., 2^k – 2^(2 – 1) } zaczynając od 2^1 Krążek nr. 3 przemieszcza się co 2^3 przeniesienie { 4, 12, 20, 28, ..., 2^k – 2^(3 – 1) } zaczynając od 2^2 ... Krążek nr. n przemieszcza się co 2^n przeniesienie { 2^n – 2^(n – 1) } zaczynając od 2^(n – 1) int ilosc_przeniesien = potega(ilosc_do_przelozenia) - 1; for (int j = 1; j <= ilosc_przeniesien; j++) { // Sprawdzam ktory krazek mam przesunac // k to jest krazek do przeniesienia int k = 0; while (true) { int z = potega(k); int y = z * 2; // // // if { Na podstawie numeru przeniesienia obliczam ktory krazek ma zosrac przeniesiony. Robie to za pomoca wzory j - 2^k % 2^(k+1) == 0 gdzie z = 2^k , y = 2^(k+1) (((j - z) % y) == 0) // Przesuwam krazek krazek[k] += ((k + 1) % 2) ? 2 : 1; krazek[k] = krazek[k] % 3; break; } } } k++; Testy dla n krążków i k słupków Ilość kr. 3 słupki 4 słupki 5 słupków 6 słupków 7 słupków 8 słupków 9 słupków 1 1 1 1 1 1 1 1 2 3 3 3 3 3 3 3 3 7 7 5 5 5 5 5 4 15 15 9 7 7 7 7 5 31 31 15 11 9 9 9 6 63 63 27 17 13 11 11 7 127 127 49 27 19 15 13 8 255 255 93 47 29 21 17 9 513 513 179 85 47 31 23 10 1023 1023 351 159 83 49 33 11 2047 2047 693 307 153 83 51 12 4095 4095 1377 601 291 151 83 13 8191 8191 2743 1187 565 285 151 14 16383 16383 5475 2359 1113 551 283 15 32767 32767 10937 4701 2207 1081 545 Ilość przeniesień Ilość przeniesień 35000 32500 30000 27500 25000 22500 20000 3 słupki 4 słupki 5 słupków 6 słupków 7 słupków 8 słupków 9 słupków 17500 15000 12500 10000 7500 5000 2500 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Ilość krążków Rozwiązywanie problemu Aby przenieść n-krążkową wieżę z jednego słupka na inny, należy podzielić słupki na 3 bazowe i k – 3 słupki pomocnicze. Z n – krążkowej kupki zdejmujemy n – (k – 3) krążki na inny słupek pomocniczy, a pozostałe krążki rozkładamy na słupki pomocnicze tak aby na każdym leżał tylko jeden krążek. Następnie składamy krążki z słupków pomocniczych na słupek docelowy, który jest wybrany ze słupków pomocniczych. Krok ten powtarzamy, aż n-krążkowa wieża nie zostanie przeniesiona na słupek docelowy. Analiza algorytmu dla 3 słupków Wieżę Hanoi z n krążkami oznaczmy przez h n . Z przeprowadzonych testów h 1=21 −1 ; h 2=2 2−1 ; h 3=2 3−1 ; ... , gdzie potęga jest równa można zauważyć, że: indeksowi liczby h . Na tej podstawie można przypuszczać, że h n=2n−1 dla dowolnej liczby krążków n. Z rekurencyjnego algorytmu rozwiązywania łamigłówki wieża Hanoi wynika następująca zależność rekurencyjna między liczbami: 1 dla n=1 h n= 2∗h n−11 dla n1 Powyższą zależność możemy sprawdzić indukcyjnie. Wiemy, że: h 1=21 −1 =1 , załóżmy że h n=2n−1 , a zatem h n1=2∗hn1 =2∗ 2n −11 =2 n1−1 Udowodniliśmy zatem, że złożoność algorytmu wynosi O 2n . { } Analiza algorytmu dla 4 słupków Choć dla 4 słupków algorytm zachowuje się inaczej niż dla 3 to złożoność obliczeniowa jest identyczna z tą dla 3 słupków. Wynika to z tego, iż dla np. n krążków pierwszym krokiem dla przeniesienia ich na inny słupek jest przeniesienie n – 1 krążków metodą wieży Hanoi dla 3 słupków, a następnie n-tego krążka. Krok ten powtarza się dopóki cała kupka nie zostanie przeniesiona. Dla przykładu przeniosę kupkę n = 5 31 =h5=h 41h31h 21 h11h 1=1517131111 =31 Analiza algorytmu dla 5 słupków Analizując wyniki algorytmu dla 5 słupków dochodzimy do wzoru rekurencyjnego w postaci: 2∗n−1 dla n=1..3 h n= 2∗hn−1−n2 dla n=2∗k 3, k ∈Z 2∗hn−1−n3 dla n=2∗k 2, k ∈Z { } Niestety nie udało mi się uprościć tej funkcji rekurencyjnej, ale pomimo tego można na zauważyć złożoność algorytmu dla 5 słupków. Dla przykładu: h 6=2∗h5 −n3 =2∗ 2∗h 4−n2−n3 =2∗ 2∗2∗ h3 −n3−n2−n3 = =2∗2∗2∗2∗3 −1−n3−n2−n3 =2∗2∗ 22∗3 −2 −n3−n2−n3 = =2∗2∗2 2∗3 −n1−n2−n3 =2∗23∗3 −2∗n2 −n2−n3 = =2∗23∗3 −3∗n4−n3 =2 4∗3 −6∗n8 −n3 =3∗24 −7∗n11 Obcinając końcówkę możemy uogólnić wzór i podpierając się notacją, O złożoność algorytmu wyznaczamy przez O3∗2 n=3 ∗O 2n Wnioski „Właściwy” algorytm wież Hanoi, a więc dla 3 słupków jest algorytmem optymalnym i ma złożoność O 2n . Wraz z dokładaniem słupków wydajność wzrasta, a ilość przełożeń potrzebnych do wykonania algorytmu maleje. Niestety prezentowane rozwiązanie nie należy do optymalnych, należy jednak zwrócić uwagę, iż nie znaleziono dotychczas algorytmu, który dokonał by przełożenia wszystkich krążków w minimalnej liczbie ruchów. Akceptowane będą wszystkie rozwiązania, które dadzą wynik nie gorszy od naszego i będą miały nie gorszą złożoność.