Rekursion
Kurz gesagt: Rekursion ist eine Methode, die sich selbst mit einer kleineren Aufgabe aufruft, bis sie den Basisfall erreicht: einen Fall, der direkt gelöst wird, ohne neuen Aufruf.
Problem
Wie viele Anordnungen gibt es?
Im Regal stehen 5 Bücher. Auf wie viele Arten kannst du sie anordnen? Für den ersten Platz gibt es 5 Möglichkeiten, für den zweiten 4, dann 3, 2 und 1: 5 · 4 · 3 · 2 · 1 = 120. Das ist die Fakultät, geschrieben 5!.
Mit einer Schleife kannst du das schon:
static int FactorialLoop(int n){ int result = 1; for (int i = 2; i <= n; i++) { result *= i; } return result;}
Console.WriteLine(FactorialLoop(5));120Sieh es auch so: 5! = 5 · 4!. Und 4! = 4 · 3!. Die Aufgabe wird über dieselbe Aufgabe, nur kleiner gelöst. Kann die Methode genau das sagen?
Die Idee
Eine Methode, die sich selbst aufruft
Sie kann. Eine Methode, die sich selbst aufruft, nennt man rekursiv, und das Vorgehen Rekursion.
static int Factorial(int n){ if (n == 1) { return 1; } return n * Factorial(n - 1);}
Console.WriteLine(Factorial(5));120Zwei Dinge sorgen dafür, dass es funktioniert:
- Ein Basisfall. Ein Fall, der direkt gelöst wird, ohne neuen Aufruf. Hier: 1! = 1.
- Eine kleinere Aufgabe. Jeder Aufruf ruft sich selbst mit einer Zahl auf, die näher am Basisfall liegt:
n - 1.
Sieh es dir an
Der Stapel der Aufrufe
static int Factorial(int n){ if (n == 1) { return 1; } return n * Factorial(n - 1);}
Console.WriteLine(Factorial(3)); // 6Schritt für Schritt, in Worten
- Main ruft Factorial(3) auf.
- Neuer Speicher für Factorial mit n = 3.
- 3 ist nicht 1, also ruft die Methode Factorial(2) auf und wartet auf die Antwort.
- Dasselbe noch einmal: Factorial(2) ruft Factorial(1) auf. Jeder Aufruf hat sein eigenes n.
- n ist 1: Das ist der Basisfall. Er gibt 1 zurück, ohne sich noch einmal aufzurufen.
- Die Antwort geht nach unten zurück: 2 * 1 = 2.
- Und noch einmal: 3 * 2 = 6.
- Factorial(3) gibt 6 zurück, und Main gibt es aus.
Jeder Aufruf bekommt seinen eigenen Speicher mit seinem eigenen n. Sie stapeln sich übereinander und warten. Diesen Stapel nennt man Aufrufstapel (call stack). Wenn der Basisfall 1 zurückgibt, gehen die Antworten eine nach der anderen nach unten zurück, und der Stapel löst sich auf.
Probier es aus
Runter und wieder rauf
Was gibt das Programm aus?
static void Steps(int n){ if (n == 0) { return; } Console.WriteLine("runter " + n); Steps(n - 1); Console.WriteLine("rauf " + n);}
Steps(2);Die Zeile vor dem rekursiven Aufruf läuft auf dem Weg nach unten. Die Zeile danach wartet, bis der kleinere Aufruf fertig ist. Deshalb kommen die „rauf“-Zeilen in umgekehrter Reihenfolge: Der letzte Aufruf kehrt als erster zurück.
Probier es aus
Finde den Fehler
SumTo(4) soll 1 + 2 + 3 + 4 = 10 zurückgeben. Stattdessen bricht das Programm mit Stack overflow ab. In welcher Zeile ist der Fehler?
Wähle die Zeile mit dem Fehler.
Die Methode ruft sich selbst mit demselben n auf, nicht mit einem kleineren. Die Aufgabe wird nicht kleiner, der Basisfall kommt nie, und der Stapel läuft über. Die Korrektur ist SumTo(n - 1).
Die Idee
Rekursion oder Schleife?
Die Fakultät lässt sich mit einer Schleife oder mit Rekursion schreiben. Hier ist die Schleife die bessere Wahl: Sie häuft nicht für jeden Aufruf Speicher an, und für 100 000 Aufrufe reicht der Stapel nicht.
Rekursion ist dort natürlich, wo eine Sache kleinere Sachen derselben Art enthält: ein Ordner mit Ordnern darin, eine Kategorie mit Unterkategorien, ein Ast mit Ästen. Solchen Aufgaben begegnest du später im Kurs. Dort wird eine Schleife umständlich, während die rekursive Methode kurz bleibt.
Genauer, für Neugierige
- Die Fakultät schreibt man oft mit dem Basisfall 0! = 1 (
if (n == 0)). Dann funktioniert sie auch für 0.
- Ein
intreicht bis 12!. Für 13! passt das Ergebnis nicht mehr hinein, und die Zahl kommt falsch heraus. Für größere Werte nimmlong.
Probier es aus
Bring die Zeilen in die richtige Reihenfolge
Die Methode soll eine Potenz berechnen: Power(2, 10) ist 1024. Bring ihre Zeilen in die richtige Reihenfolge.
return b * Power(b, exp - 1);{}static int Power(int b, int exp){}if (exp == 0)return 1;
Übung
Rekursive Aufgaben
1. Fakultät auf zwei Arten. Schreib FactorialLoop(int n) mit einer for-Schleife und Factorial(int n) mit Rekursion. Lies eine Zahl ein und gib das Ergebnis beider Methoden aus.
5Die Fakultät von 5 ist 120Die Fakultät von 5 ist 1202. Quersumme. SumDigits(int n): die letzte Ziffer (n % 10) plus die Quersumme des Rests (n / 10). Der Basisfall ist eine einstellige Zahl.
3. Fibonacci. Jede Zahl ist die Summe der zwei davor: 0, 1, 1, 2, 3, 5, 8… Fib(int n) gibt die n-te Zahl zurück. Probier es mit 10 und dann mit 45. Warum ist es so langsam? Hinweis: Wie oft wird jede Zahl ausgerechnet?
4. Palindrom. IsPalindrome(string s): Der erste und der letzte Buchstabe sind gleich, und die Mitte ist ein Palindrom. Der Basisfall ist ein Wort mit 0 oder 1 Buchstaben.
SumDigits(2024)Fib(10)IsPalindrome("neben")IsPalindrome("ABCDE")855TrueFalseTest
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Bedingungen · Schleifen · Methoden · Parameter und return
- Geht weiter in: Exceptions · Werfen und eigene Exceptions · Composite · Big-O: wie die Arbeit wächst
- Aufgaben zum Thema: Methoden