Rekursion
Einen Turm von Hanoi mit 64 Scheiben zu versetzen, kostet 18 446 744 073 709 551 615 Züge; eine rekursive Funktion beschreibt die Lösung in vier Zeilen.
Eine Funktion, die sich selbst aufruft
Manche Probleme enthalten eine kleinere Kopie ihrer selbst. Die Fakultät von 5, geschrieben 5!, ist 5 × 4 × 3 × 2 × 1 = 120. Schau noch einmal hin: Das ist 5 × (4 × 3 × 2 × 1), also 5 × 4!. Allgemein gilt n! = n × (n − 1)!. Um eine Fakultät zu berechnen, kannst du also eine kleinere Fakultät berechnen und multiplizieren. Eine Funktion, die ein Problem löst, indem sie sich selbst mit einer kleineren Version aufruft, ist rekursiv.
function factorial(n) {
if (n <= 1) return 1; // Basisfall: hier aufhören
return n * factorial(n - 1); // Rekursionsfall: ein kleineres Problem
}
console.log(factorial(5)); // 120Jede rekursive Funktion braucht zwei Teile. Der Basisfall (Rekursionsanker) ist eine Eingabe, die klein genug ist, um sie direkt zu beantworten, ohne weiteren Aufruf: hier ist die Fakultät von 1 gleich 1. Der Rekursionsfall verkleinert das Problem und ruft die Funktion erneut auf. Jeder Aufruf muss dem Basisfall näherkommen. Tut er das nicht, etwa wenn du aus Versehen factorial(n + 1) schreibst, enden die Aufrufe nie.
Der Aufrufstapel
Wie behält der Computer all die unfertigen Aufrufe im Blick? Wenn factorial(5) läuft, kommt es zu 5 * factorial(4) und muss auf die Antwort warten. Der Computer legt den wartenden Aufruf auf einen Stapel (Stack), einen Haufen, bei dem das zuletzt Hingelegte zuerst wieder heruntergenommen wird. factorial(4) wartet auf factorial(3), und so weiter bis factorial(1), das sofort 1 zurückgibt. Dann wird der Stapel abgebaut: factorial(2) gibt 2 zurück, factorial(3) gibt 6 zurück, factorial(4) gibt 24 zurück und factorial(5) gibt 120 zurück.
Der Stapel hat eine begrenzte Größe. Eine Rekursion, die ihren Basisfall nie erreicht, fügt so lange Aufrufe hinzu, bis der Platz ausgeht, und JavaScript bricht mit einem Fehler ab. In Chrome und Node.js lautet die Meldung "RangeError: Maximum call stack size exceeded". Dieselbe Grenze bedeutet, dass sehr tiefe Rekursion, mit Hunderttausenden Ebenen, besser als Schleife geschrieben wird.
Die Türme von Hanoi
Die Türme von Hanoi sind ein Rätsel, das der französische Mathematiker Édouard Lucas 1883 veröffentlichte. Es gibt drei Stäbe und auf dem ersten einen Stapel unterschiedlich großer Scheiben, die größte unten. Ziel ist, den ganzen Stapel auf den dritten Stab zu bringen. Du darfst immer nur eine Scheibe bewegen, nur die oberste eines Stabs, und nie eine größere Scheibe auf eine kleinere legen.
Schritt für Schritt zu überlegen, wohin jede Scheibe kommt, wird schnell verwirrend. Rekursiv gedacht wird es kurz. Um n Scheiben von A nach C zu bringen: Bring zuerst die oberen n − 1 Scheiben aus dem Weg, von A nach B. Bring dann die größte Scheibe von A nach C. Bring dann die n − 1 Scheiben von B auf sie nach C. n − 1 Scheiben zu versetzen, ist dasselbe Rätsel, eine Nummer kleiner, und eine Scheibe zu versetzen, ist der Basisfall. Bei 3 Scheiben ergibt das 7 Züge. Das Labor zeigt bei jedem Zug den Stapel der wartenden Aufrufe.
function hanoi(n, from, to, via) {
if (n === 0) return;
hanoi(n - 1, from, via, to);
console.log("disk " + n + ": " + from + " -> " + to);
hanoi(n - 1, via, to, from);
}
hanoi(3, "A", "C", "B"); // 7 ZügeZum Drehen ziehen. Zum Zoomen scrollen oder mit zwei Fingern ziehen.
Wie viele Züge brauchen n Scheiben? n Scheiben zu versetzen, kostet zweimal n − 1 Scheiben plus einen Zug: M(n) = 2M(n − 1) + 1, mit M(1) = 1. Das ergibt 1, 3, 7, 15, 31, also M(n) = 2ⁿ − 1, und keine Lösung kommt mit weniger aus. Zehn Scheiben brauchen 1023 Züge. Vierundsechzig Scheiben brauchen 2⁶⁴ − 1 = 18 446 744 073 709 551 615 Züge. Bei einem Zug pro Sekunde sind das etwa 585 Milliarden Jahre. Das ist das O(2ⁿ)-Wachstum aus der letzten Einheit.
Rekursion, Schleifen und doppelte Arbeit
Alles, was mit Rekursion geschrieben ist, lässt sich auch mit einer Schleife und einem eigenen Stapel schreiben, und einfache Fälle wie die Fakultät sind als Schleife oft klarer. Rekursion glänzt, wenn das Problem selbst wie ein Baum geformt ist: das Teilen einer Liste bei Mergesort, das Durchsuchen von Ordnern in Ordnern oder die Suche in einem Labyrinth, um die es in der letzten Einheit geht. Eine Falle ist doppelte Arbeit. Die Fibonacci-Zahlen 0, 1, 1, 2, 3, 5, 8, … folgen fib(n) = fib(n − 1) + fib(n − 2). Direkt als Rekursion geschrieben, macht fib(10) = 55 genau 177 Aufrufe, fib(20) macht 21 891 und fib(30) macht 2 692 537, weil dieselben kleineren Werte immer wieder berechnet werden. Sich schon gefundene Antworten zu merken oder mit einer Schleife von unten aufzubauen, beseitigt diese Verschwendung.
| Rekursion | Eine Funktion, die ein Problem löst, indem sie sich selbst mit einer kleineren Version aufruft |
| Basisfall | Eine Eingabe, die direkt beantwortet wird und die Aufrufe beendet |
| Rekursionsfall | Der Teil, der das Problem verkleinert und erneut aufruft |
| Aufrufstapel | Der Stapel der Aufrufe, die auf eine Antwort warten, last in, first out |
| Stapelüberlauf (Stack Overflow) | Zu viele wartende Aufrufe: RangeError in JavaScript |
| Fakultät n! | n × (n − 1) × … × 1; 5! = 120 |
| Türme von Hanoi | Lucas, 1883: 2ⁿ − 1 Züge bei n Scheiben |
- Wie viel ist 6! (6 Fakultät)?
- Wie viele Züge brauchen die Türme von Hanoi mit 4 Scheiben?
- Wie viele Züge mit 10 Scheiben?
- Was macht der Basisfall?
- Er beantwortet eine kleine Eingabe direkt und beendet die Kette der Aufrufe
- Er lässt die Funktion schneller laufen
- Er ruft die Funktion mit einer größeren Eingabe auf
- Er gibt das Ergebnis aus
- Was passiert, wenn eine rekursive Funktion ihren Basisfall nie erreicht?
- Sie gibt 0 zurück
- Der Aufrufstapel läuft voll, und das Programm bricht mit einem Fehler ab
- Sie läuft still und ohne Fehler endlos weiter
- JavaScript wählt für dich einen Basisfall
- In welcher Reihenfolge nimmt ein Stapel Elemente herunter?
- Last in, first out (zuletzt hinein, zuerst heraus)
- First in, first out (zuerst hinein, zuerst heraus)
- Das Kleinste zuerst
- Zufällig
- Während factorial(5) gerade factorial(1) berechnet: Wie viele factorial-Aufrufe liegen auf dem Stapel, diesen eingeschlossen?
- Was ist der erste Schritt des rekursiven Plans, um n Scheiben von A nach C zu bringen?
- Die oberen n − 1 Scheiben von A nach B bringen
- Die größte Scheibe nach C bringen
- Die kleinste Scheibe nach C bringen
- Alle Scheiben nach B bringen
- Wer veröffentlichte 1883 das Rätsel der Türme von Hanoi?
- Leonhard Euler
- Édouard Lucas
- Tony Hoare
- Euklid
- Das einfache rekursive fib macht für fib(10) genau 177 Aufrufe. Wie viel ist fib(10)?
- Warum ist das einfache rekursive fib langsam?
- Es berechnet dieselben kleineren Werte viele Male
- Rekursion ist immer langsam
- Additionen sind teuer
- Es hat keinen Basisfall
- Eine rekursive Funktion ruft sich selbst mit einer kleineren Version des Problems auf.
- Sie braucht einen Basisfall, der die Aufrufe beendet.
- Wartende Aufrufe liegen auf dem Aufrufstapel, last in, first out.
- Türme von Hanoi: 2ⁿ − 1 Züge; 64 Scheiben würden bei einem Zug pro Sekunde etwa 585 Milliarden Jahre dauern.
- Auf doppelte Arbeit achten, wie beim einfachen rekursiven Fibonacci.
Quellen zu diesem Kurs (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
- Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
- with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
- MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
- D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
- Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
- Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
- Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
- Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
- Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.
Abgerufen: 2026-10-17