1
Polecenie 1

Zapisz program wykorzystujący rekurencję, który wypisze kolejne kroki, jakie trzeba wykonać, aby przenieść wieżę składającą się z n krążków o różnej średnicy ze słupka A na słupek C. Swój program przetestuj dla 5 krążków.

Zasady gry:

  • Gra składa się z trzech słupków i zestawu krążków o różnych średnicach.

  • Na jednym słupku umieszczona jest wybudowana wieża.

  • Celem gry jest odbudowanie wieży na trzecim słupku.

  • Nie wolno położyć krążka o większej średnicy na krążku o mniejszej średnicy.

  • Na raz można przenosić tylko jeden krążek (znajdujący się na szczycie wieży).

  • Do przenoszenia można wykorzystać dodatkowy słupek.

Specyfikacja problemu:

Dane:

  • n – liczba krążków w grze, liczba naturalna

  • A, B, C – symboliczne nazwy słupków, znaki

Wynik:

Program, na wyjściu standardowym, wypisuje w kolejnych linijkach kroki, które prowadzą do rozwiązania zagadki Wież Hanoi.

R1XRVxyQa8b4t
Wymyśl pytanie na kartkówkę związane z tematem materiału.
1
Polecenie 2

Porównaj swoje rozwiązanie z przedstawionym w filmie.

R1bVBYyDekoQo1
Film nawiązujący do treści materiału zagadki wież Hanoi.

Kod programu zaprezentowanego w filmie:

RAF3jvykLISuk

Przycisk umożliwiający pobranie poiku TXT z kodem z filmu.

Plik TXT o rozmiarze 1.36 KB w języku polskim

Przedstawienie problemu

Wiesz już, że zagadka Wież Hanoizagadka Wież Hanoizagadka Wież Hanoi polega na przeniesieniu krążków o różnych rozmiarach z jednego słupka na drugi. Warunkiem poprawnego rozwiązania zadania jest pojedyncze przenoszenie każdego krążka, a także brak możliwości położenia większego krążka na mniejszym. Do dyspozycji mamy jednak pomocniczy słupek, na który możemy tymczasowo odkładać krążki.

Mamy zatem trzy słupki: A, B, C oraz n krążków. Początkowo wszystkie krążki położone są na słupku A, w kolejności od największego do najmniejszego. Docelowo wszystkie krążki mają znaleźć się na słupku C w tej samej kolejności, w jakiej znajdowały się na słupku A. Pomocniczym słupkiem jest słupek B. Celem naszego zadania jest przedstawienie ciągu ruchów, opisującego rozwiązanie zagadki Wież Hanoi.

R18lEEYeXYasj
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Implementacja rozwiązania w języku Java

Problem Wież Hanoi można rozwiązać w sposób iteracyjny, czyli za pomocą pętli. Jest on jednak bardzo dobrym przykładem sytuacji, gdzie możemy zastosować rozwiązanie rekurencyjnefunkcja rekurencyjnarekurencyjne, czyli takie, w którym używamy metody „dziel i zwyciężaj”. Operacje niezbędne do umieszczenia coraz mniejszych krążków na słupku docelowym (C) są takie same jak w przypadku pierwszego, największego krążka – z tym zastrzeżeniem, że zmienia się słupek pomocniczy. Najpierw (czyli po przeniesieniu największego krążka na słupek C) będzie nim A. Później rolę słupka pomocniczego będą na przemian odgrywać słupki BA. Zauważmy, że słupek docelowy się nie zmienia.

Przypadkiem rekurencyjnymprzypadek rekurencyjnyPrzypadkiem rekurencyjnym będzie sytuacja, w której na słupku A pozostał co najmniej jeden krążek. Instrukcje składające się na funkcję rekurencyjną hanoi() będą wówczas wykonywane. Przypadkiem bazowymprzypadek bazowyPrzypadkiem bazowym będzie sytuacja, w której na słupku A nie ma już żadnego krążka. Aktualne wywołanie funkcji hanoi() przestanie być wtedy wykonywane.

Ważne!

Należy pamiętać, aby w funkcji rekurencyjnej zawsze opisać przypadek bazowy, który określi, kiedy funkcja powinna się zatrzymać. Bez zdefiniowanego przypadku bazowego funkcja teoretycznie będzie wywoływać samą siebie w nieskończoność. W praktyce nastąpi moment, w którym zostaną wyczerpane zasoby przydzielone programowi, co zakończy się jego awarią. Taką sytuację nazywamy przepełnieniem stosu.

Przepełnienie stosu (ang. Stack overflow) polega na wyczerpaniu miejsca zarezerwowanego na stos. Spowodowane jest ono właśnie nadmierną bądź nieskończoną rekurencją. Kiedy używamy języka Java, mamy domyślnie do dyspozycji stos o rozmiarze 512 KB, a przekroczenie tej wartości sygnalizowane jest zgłoszeniem błędu StackOverflowError.

Oto implementacja algorytmu w języku Java dla 5 krążków:

Linia 1. public class Main otwórz nawias klamrowy. Linia 2. prawy ukośnik prawy ukośnik n – liczba krążków przecinek A – miejsce początkowe. Linia 3. prawy ukośnik prawy ukośnik B – słupek pomocniczy przecinek C – miejsce docelowe. Linia 4. public static void hanoi otwórz nawias okrągły int n przecinek char A przecinek char B przecinek char C zamknij nawias okrągły otwórz nawias klamrowy. Linia 5. if otwórz nawias okrągły n zamknij nawias ostrokątny 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. prawy ukośnik prawy ukośnik wywołanie rekurencyjne. Linia 7. hanoi otwórz nawias okrągły n minus 1 przecinek A przecinek C przecinek B zamknij nawias okrągły średnik. Linia 8. System kropka out kropka println otwórz nawias okrągły cudzysłów Przeniesienie z cudzysłów plus A plus cudzysłów do cudzysłów plus C zamknij nawias okrągły średnik. Linia 9. prawy ukośnik prawy ukośnik wywołanie rekurencyjne. Linia 10. hanoi otwórz nawias okrągły n minus 1 przecinek B przecinek A przecinek C zamknij nawias okrągły średnik. Linia 11. zamknij nawias klamrowy. Linia 12. zamknij nawias klamrowy. Linia 14. public static void main otwórz nawias okrągły String otwórz nawias kwadratowy zamknij nawias kwadratowy args zamknij nawias okrągły otwórz nawias klamrowy. Linia 15. hanoi otwórz nawias okrągły 5 przecinek apostrof A apostrof przecinek apostrof B apostrof przecinek apostrof C apostrof zamknij nawias okrągły średnik. Linia 16. zamknij nawias klamrowy. Linia 17. zamknij nawias klamrowy.

Po uruchomieniu programu otrzymamy następujący wynik:

Linia 1. Przeniesienie z A do C. Linia 2. Przeniesienie z A do B. Linia 3. Przeniesienie z C do B. Linia 4. Przeniesienie z A do C. Linia 5. Przeniesienie z B do A. Linia 6. Przeniesienie z B do C. Linia 7. Przeniesienie z A do C. Linia 8. Przeniesienie z A do B. Linia 9. Przeniesienie z C do B. Linia 10. Przeniesienie z C do A. Linia 11. Przeniesienie z B do A. Linia 12. Przeniesienie z C do B. Linia 13. Przeniesienie z A do C. Linia 14. Przeniesienie z A do B. Linia 15. Przeniesienie z C do B. Linia 16. Przeniesienie z A do C. Linia 17. Przeniesienie z B do A. Linia 18. Przeniesienie z B do C. Linia 19. Przeniesienie z A do C. Linia 20. Przeniesienie z B do A. Linia 21. Przeniesienie z C do B. Linia 22. Przeniesienie z C do A. Linia 23. Przeniesienie z B do A. Linia 24. Przeniesienie z B do C. Linia 25. Przeniesienie z A do C. Linia 26. Przeniesienie z A do B. Linia 27. Przeniesienie z C do B. Linia 28. Przeniesienie z A do C. Linia 29. Przeniesienie z B do A. Linia 30. Przeniesienie z B do C. Linia 31. Przeniesienie z A do C.

Jest to gotowe rozwiązanie problemu Wież Hanoi dla 5 krążków.

Złożoność czasowa i pamięciowa

Jak możemy zauważyć, do rozwiązania problemu Wież Hanoi dla 5 krążków potrzebne było 31 ruchów. Gdybyśmy uruchomili ten program dla 10 krążków, potrzebne byłyby aż 1023 ruchy.

Aby rozwiązać problem Wież Hanoi dla  krążków, należy przenieść jeden krążek oraz rozwiązać dwukrotnie ten problem dla . Oznacza to, że liczba wykonanych przeniesień jest równa . Algorytm ma zatem złożoność wykładniczą .

Złożoność pamięciowa tego algorytmu jest liniowa, czyli równa . Wynika to z faktu, że funkcje wywołane w jednej funkcji rekurencyjnej nie wywołają się jednocześnie — drugie wywołanie pojedynczej funkcji będzie miało miejsce dopiero po zakończeniu całości pierwszego wywołania. Zatem pamięć ta może być użyta ponownie. W tym samym momencie aktywnych będzie maksymalnie funkcji.

Słownik

funkcja rekurencyjna
funkcja rekurencyjna

funkcja, która wywołuje samą siebie, aż do momentu osiągnięcia stanu początkowego

myślenie rekurencyjne
myślenie rekurencyjne

myślenie polegające na tym, że wartość pewnej wielkości obliczana jest poprzez odwołanie się do tej samej wielkości określonej dla mniejszych wartości parametrów

przypadek bazowy
przypadek bazowy

warunek w funkcji rekurencyjnej, po którego spełnieniu funkcja nie wywołuje samej siebie po raz kolejny

przypadek rekurencyjny
przypadek rekurencyjny

warunek w funkcji rekurencyjnej, po którego spełnieniu funkcja ponownie wywołuje siebie samą

zagadka Wież Hanoi
zagadka Wież Hanoi

problem polegający na przeniesieniu wieży z krążków o różnej średnicy z jednego słupka na drugi, przy czym można przenosić tylko po jednym krążku z wierzchu stosu; nie jest dozwolone położenie większego krążka na mniejszym

złożoność wykładnicza
złożoność wykładnicza

złożoność, która jest określona za pomocą funkcji wykładniczej, rośnie wykładniczo w zależności od rozmiaru danych wejściowych n i można ją zapisać jako Okn