Türme von Hanoi
Was sind die Türme von Hanoi?
Die Türme von Hanoi sind ein klassisches mathematisches Rätsel und ein Paradebeispiel für rekursive Algorithmen in der Informatik. Das Puzzle wurde 1883 vom französischen Mathematiker Édouard Lucas erfunden.
Das Spiel besteht aus:
- 3 Stäben (Start, Hilfs- und Zielstab)
- n Scheiben unterschiedlicher Größe, die auf dem Startstab gestapelt sind (größte unten, kleinste oben) Ziel: Alle Scheiben vom Startstab auf den Zielstab versetzen – unter folgenden Regeln:
- Pro Zug darf nur eine Scheibe bewegt werden.
- Eine größere Scheibe darf niemals auf einer kleineren liegen.
- Alle drei Stäbe dürfen als Zwischenlager genutzt werden.
Warum ist Hanoi wichtig für die Informatik?
Das Hanoi-Problem ist kein bloßes Spielzeug. Es veranschaulicht zentrale Konzepte:
| Konzept | Bedeutung in Hanoi |
|---|---|
| Rekursion | Das Problem wird auf kleinere Teilprobleme zurückgeführt |
| Teile und Herrsche | n-1 Scheiben zuerst wegräumen, dann die größte bewegen |
| Exponentielles Wachstum | Minimale Züge: 2ⁿ − 1 |
| Rekursionsbaum | Visualisiert alle Teilaufrufe und deren Abhängigkeiten |
| Call Stack | Jeder rekursive Aufruf legt einen neuen Frame auf den Stack |
Der Algorithmus
Grundidee
Um n Scheiben von Stab A nach Stab C zu bewegen (mit B als Hilfsstab):
- Bewege die oberen
n-1Scheiben vonAnachB(mitCals Hilfe). - Bewege die größte Scheibe von
AnachC. - Bewege die
n-1Scheiben vonBnachC(mitAals Hilfe).
Pseudocode
hanoi(n, start, ziel, hilf):
wenn n == 1:
bewege Scheibe 1 von start nach ziel
sonst:
hanoi(n-1, start, hilf, ziel)
bewege Scheibe n von start nach ziel
hanoi(n-1, hilf, ziel, start)
Haskell-Notation (funktional)
hanoi n start ziel hilf =
if (n == 1)
then move n start ziel
else hanoi (n-1) start hilf ziel
++ move n start ziel
++ hanoi (n-1) hilf ziel start
Java-Implementierung
Minimale Version
public class Hanoi {
public static void hanoi(int n, char start, char ziel, char hilf) {
if (n == 1) {
System.out.println("Scheibe 1: " + start + " → " + ziel);
return;
}
hanoi(n - 1, start, hilf, ziel);
System.out.println("Scheibe " + n + ": " + start + " → " + ziel);
hanoi(n - 1, hilf, ziel, start);
}
public static void main(String[] args) {
int n = 3;
System.out.println("Türme von Hanoi mit " + n + " Scheiben:");
hanoi(n, 'A', 'C', 'B');
}
}
Ausgabe für n = 3:
Türme von Hanoi mit 3 Scheiben:
Scheibe 1: A → C
Scheibe 2: A → B
Scheibe 1: C → B
Scheibe 3: A → C
Scheibe 1: B → A
Scheibe 2: B → C
Scheibe 1: A → C
Erweiterung: Züge als Liste zurückgeben
import java.util.ArrayList;
import java.util.List;
public class HanoiListe {
public record Zug(int scheibe, char von, char nach) {
@Override
public String toString() {
return "Scheibe " + scheibe + ": " + von + " → " + nach;
}
}
public static List<Zug> hanoi(int n, char start, char ziel, char hilf) {
List<Zug> züge = new ArrayList<>();
if (n == 1) {
züge.add(new Zug(1, start, ziel));
return züge;
}
züge.addAll(hanoi(n - 1, start, hilf, ziel));
züge.add(new Zug(n, start, ziel));
züge.addAll(hanoi(n - 1, hilf, ziel, start));
return züge;
}
public static void main(String[] args) {
int n = 4;
List<Zug> züge = hanoi(n, 'A', 'C', 'B');
System.out.println("Anzahl Züge: " + züge.size()); // 15
züge.forEach(System.out::println);
}
}
Rekursionsbaum
Für n = 3 entsteht folgender Rekursionsbaum (Aufrufe):
hanoi(3, A, C, B)
├── hanoi(2, A, B, C)
│ ├── hanoi(1, A, C, B) → Scheibe 1: A → C
│ ├── Scheibe 2: A → B
│ └── hanoi(1, C, B, A) → Scheibe 1: C → B
├── Scheibe 3: A → C
└── hanoi(2, B, C, A)
├── hanoi(1, B, A, C) → Scheibe 1: B → A
├── Scheibe 2: B → C
└── hanoi(1, A, C, B) → Scheibe 1: A → C
Knoten im Baum: 2ⁿ⁺¹ − 1 = 7 Knoten (für n=3)
Anzahl Züge: 2ⁿ − 1 = 7 Züge (für n=3)
Komplexitätsanalyse
Zeitkomplexität
Für n Scheiben gilt die Rekurrenzrelation:
T(1) = 1
T(n) = 2 · T(n-1) + 1
Lösung: T(n) = 2ⁿ − 1
| n | Züge (2ⁿ − 1) |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 7 |
| 4 | 15 |
| 5 | 31 |
| 10 | 1.023 |
| 20 | 1.048.575 |
| 64 | 18.446.744.073.709.551.615 |
Legende: Das Hanoi-Problem mit 64 Scheiben würde – bei einem Zug pro Sekunde – über 580 Milliarden Jahre dauern.
Speicherkomplexität
Die Rekursionstiefe beträgt n, daher ist der Call-Stack-Verbrauch O(n).
Iterative Lösung
Das Hanoi-Problem lässt sich auch ohne Rekursion lösen – mit einer bitweisen Strategie:
public class HanoiIterativ {
public static void hanoi(int n) {
long züge = (1L << n) - 1; // 2^n - 1
char[] stäbe = (n % 2 == 0)
? new char[]{'A', 'B', 'C'}
: new char[]{'A', 'C', 'B'};
for (long i = 1; i <= züge; i++) {
int scheibe = Long.numberOfTrailingZeros(i) + 1;
char von = stäbe[(int)((i / scheibe) % 3)];
// Vereinfachte Darstellung – vollständige Implementierung
// erfordert Zustandsverfolgung der Stäbe
System.out.println("Zug " + i + ": Scheibe " + scheibe);
}
}
}
Hinweis: Die iterative Variante ist technisch korrekt, aber schwerer zu verstehen. Für Lernzwecke ist die rekursive Version vorzuziehen.
Versionen & Abhängigkeiten
Java
- Java Version: 21 (LTS)
- Empfehlung: Java 21 oder höher verwenden, um Records und moderne Sprachfeatures zu nutzen.
Keine externen Abhängigkeiten
Dieses Projekt benötigt keine Bibliotheken – nur Standard-Java. Ideal zum Einstieg in Rekursion und Algorithmen.
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
GitHub-Repository
Den vollständigen Code – inklusive interaktiver Simulation, Rekursionsbaum-Visualisierung und Schritt-für-Schritt-Player – findest Du auf GitHub:
Fazit
Die Türme von Hanoi sind ein zeitloser Klassiker, der auf elegante Weise zeigt, wie Rekursion komplexe Probleme auf einfache Teilprobleme reduziert. Mit nur wenigen Zeilen Java-Code lässt sich ein Algorithmus implementieren, der mathematisch beweisbar optimal ist.
Wer Rekursion wirklich verstehen will, sollte Hanoi nicht überspringen – es ist das “Hello World” der algorithmischen Denkweise.
Share this content:



Post Comment