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=−1n1 , 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−11 dla n1
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 n1=2∗hn1 =2∗ 2n −11 =2 n1−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 41h31h 21 h11h 1=1517131111 =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−n2 dla n=2∗k 3, k ∈Z
2∗hn−1−n3 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 −n3 =2∗ 2∗h 4−n2−n3 =2∗ 2∗2∗ h3 −n3−n2−n3 =
=2∗2∗2∗2∗3 −1−n3−n2−n3 =2∗2∗ 22∗3 −2 −n3−n2−n3 =
=2∗2∗2 2∗3 −n1−n2−n3 =2∗23∗3 −2∗n2 −n2−n3 =
=2∗23∗3 −3∗n4−n3 =2 4∗3 −6∗n8 −n3 =3∗24 −7∗n11
Obcinając końcówkę możemy uogólnić wzór i podpierając się notacją, O złożoność
algorytmu wyznaczamy przez O3∗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ść.

Podobne dokumenty