Rekursion in der Informatik einfach erklärt
Du lernst, wie Aufgaben durch Selbstaufrufe gelöst werden, wann diese enden und wie du dasselbe Ergebnis mit einer Schleife berechnen kannst.
Kurz gesagt
Eine Funktion ist ein Programmteil, der zu einer Eingabe ein Ergebnis liefert. Bei direkter Rekursion ruft sie sich selbst auf. Ein Basisfall liefert ein Ergebnis ohne weiteren Selbstaufruf; die anderen Aufrufe müssen diesen Fall erreichen.
Ein Aufruf startet eine Funktion mit einer bestimmten Eingabe. Der Rückgabewert ist das Ergebnis, das dieser Aufruf zurückliefert. Du beschreibst hier Funktionen durch Regeln in Worten und Rechnungen und untersuchst, wie ihre Aufrufe ablaufen.
Deine Lernziele
Bei direkter Rekursion ruft eine Funktion sich selbst auf. Ihre Regeln unterscheiden einen Fall ohne weiteren Selbstaufruf und einen Fall mit Selbstaufruf.
Der Rekursionsanfang heißt auch Basisfall. Hier liefert die Funktion direkt ein Ergebnis zurück. Die Abbruchbedingung legt fest, wann dieser Fall gilt. Der Rekursionsschritt enthält den Selbstaufruf. Für einen erfolgreichen Ablauf muss dessen Eingabe so verändert werden, dass schließlich ein Basisfall erreicht wird.
Betrachte die ausgedachte Funktion „zaehle“. Ihre Eingabe n ist eine ganze Zahl ab null. Bei n=0 gibt sie direkt 0 zurück. Bei n>0 berechnet sie 1+\operatorname{zaehle}(n-1) und gibt dieses Ergebnis zurück. Die Schreibweise \operatorname{zaehle}(n-1) bedeutet: Rufe „zaehle“ mit der Eingabe n-1 auf. Der nächste Aufruf erhält also eine um eins kleinere Eingabe.
Rekursionsanfang und Rekursionsschritt
Der Rekursionsanfang liefert ein Ergebnis ohne weiteren Selbstaufruf. Der Rekursionsschritt ruft die Funktion erneut auf und kann deren Ergebnis weiterverarbeiten.
Beispiel: Die Regel von „zaehle“ anwenden
- 1Bei \operatorname{zaehle}(2) gilt die Abbruchbedingung n=0 noch nicht.
- 2Der Rekursionsschritt verlangt 1+\operatorname{zaehle}(1).
- 3Weitere Aufrufe führen zu \operatorname{zaehle}(0). Dieser Basisfall gibt direkt 0 zurück.
Teste dich
Betrachte die ausgedachte Funktion „summe“. Die Eingabe n ist eine ganze Zahl ab null. Bei n=0 gibt sie 0 zurück. Sonst berechnet sie n+\operatorname{summe}(n-1) und gibt das Ergebnis zurück.
Ein äußerer Aufruf kann seine Addition erst abschließen, wenn der innere Aufruf sein Ergebnis zurückgegeben hat. Beim Aufrufen werden die Eingaben kleiner. Sobald der Basisfall antwortet, werden die wartenden Rechnungen von innen nach außen abgeschlossen. Dabei verwendest du jeweils den Rückgabewert des inneren Aufrufs, nicht dessen Eingabe.
Der Aufrufstapel ist ein Speicher für noch nicht abgeschlossene Funktionsaufrufe und ihre benötigten Werte. Ein Stapel ist hier ein Modell: Der zuletzt begonnene, noch offene Aufruf wird zuerst abgeschlossen. Bei „summe“ muss zum Beispiel gespeichert bleiben, welche Zahl nach der Rückgabe noch addiert werden soll.
Beispiel: Den Aufruf summe mit der Eingabe drei verfolgen
- 1\operatorname{summe}(3) wartet auf \operatorname{summe}(2); danach muss noch 3 addiert werden.
- 2\operatorname{summe}(2) wartet auf \operatorname{summe}(1); danach muss noch 2 addiert werden.
- 3\operatorname{summe}(1) wartet auf \operatorname{summe}(0); danach muss noch 1 addiert werden.
- 4Der Basisfall \operatorname{summe}(0) gibt 0 zurück.
- 5Nun entstehen die Rückgabewerte 1+0=1, dann 2+1=3 und schließlich 3+3=6.
Merke: Trenne zwei Richtungen: Zuerst verfolgst du die Aufrufe bis zum Basisfall. Danach setzt du die Rückgabewerte in die wartenden Rechnungen ein.
Teste dich
Terminierung bedeutet, dass ein Ablauf nach endlich vielen Schritten endet. Prüfe dafür den erlaubten Eingabebereich, die Abbruchbedingung und die Änderung der Eingabe bei jedem Selbstaufruf. Eine vorhandene Abbruchbedingung genügt nicht: Der Ablauf muss sie tatsächlich erreichen.
Die ausgedachte Funktion „runter“ erlaubt ganze Zahlen ab null. Bei n=0 gibt sie 0 zurück. Sonst ruft sie \operatorname{runter}(n-1) auf und gibt dessen Ergebnis zurück. Von jeder erlaubten Eingabe führen endlich viele Schritte um eins zur null. Deshalb erreicht diese Funktion ihren Basisfall.
Die ausgedachte Funktion „hoch“ hat denselben Basisfall, ruft sich sonst aber mit n+1 auf. Bei einer positiven Startzahl wachsen die Eingaben und erreichen null nicht. Auch eine unveränderte positive Eingabe würde den Basisfall nicht erreichen. Ohne Basisfall entstehen bei solchen Selbstaufrufen ebenfalls weitere Aufrufe statt einer regulären Rückgabe.
In einer Umsetzung können zu viele offene Aufrufe den verfügbaren Speicher für den Aufrufstapel überschreiten. Ein solcher Überlauf heißt auch „Stack Overflow“. Das ist eine mögliche Folge und tritt nicht bei jedem rekursiven Aufruf auf.
Beachte die genaue Bedingung: n\leq0 bedeutet „n ist kleiner oder gleich null“. Dann gehören auch negative Eingaben zum Basisfall. Beim Verringern um zwei kann eine Folge die null überspringen und trotzdem einen solchen Basisfall erreichen.
Beispiel: Die Richtung der Eingaben prüfen
- 1Starte „hoch“ mit der Eingabe 2. Die Abbruchbedingung lautet n=0.
- 2Die nächsten Eingaben sind 3, 4 und 5.
- 3Jeder weitere Schritt erhöht die positive Eingabe um eins. Null wird deshalb nicht erreicht.
Merke: Prüfe die tatsächlich entstehenden Eingaben. Eine Abbruchbedingung im Text garantiert noch keinen Abbruch.
Teste dich
Die Fakultät einer positiven ganzen Zahl ist das Produkt aller ganzen Zahlen von eins bis zu dieser Zahl. Das Zeichen ! kennzeichnet die Fakultät. Zum Beispiel gilt 4!=1\cdot2\cdot3\cdot4=24. Das Zeichen \cdot steht für Multiplikation. Hier betrachten wir Eingaben n\geq1, also ganze Zahlen ab eins.
Die rekursive Funktion „fak“ gibt bei n=1 direkt 1 zurück. Für größere Eingaben berechnet sie n\cdot\operatorname{fak}(n-1). Jeder Aufruf wartet auf das kleinere Ergebnis und multipliziert es anschließend mit seiner eigenen Eingabe.
Iteration bedeutet Wiederholung durch eine Schleife. Eine Schleife führt einen Arbeitsschritt wiederholt aus. Für eine iterative Fakultätsberechnung speicherst du zunächst das Ergebnis 1. Dann multiplizierst du dieses Ergebnis der Reihe nach mit den ganzen Zahlen von 2 bis n. Nach jeder Multiplikation ersetzt du das gespeicherte Ergebnis durch das neue. Bei n=1 gibt es keinen Schleifendurchlauf; das Ergebnis bleibt 1.
Beide Verfahren bilden für die erlaubten Eingaben dasselbe Produkt. Die Schleife sammelt dabei keine wartenden Selbstaufrufe. Im beschriebenen Aufrufstapel-Modell benötigt jeder wartende rekursive Aufruf zusätzlichen Speicher. Iterative Verfahren sind meist effizienter, kommen also mit weniger Aufwand aus, und benötigen meist weniger Speicher. Für ein konkretes Programm hängt das auch von seiner Umsetzung ab.
Iteration
Bei Iteration wiederholt eine Schleife einen Arbeitsschritt. Ein gespeichertes Zwischenergebnis kann dabei Schritt für Schritt verändert werden.
| Verfahren | Ablauf für die Eingabe vier | Ergebnis |
|---|---|---|
| Rekursion | Aufrufe mit 4, 3, 2, 1; Rückgabewerte 1, 2, 6, 24. | 24 |
| Iteration | Startwert 1; danach Multiplikation mit 2, mit 3 und mit 4. | 24 |
Teste dich
Alles auf einen Blick
Rekursion
Der Basisfall gibt direkt ein Ergebnis zurück; der Rekursionsschritt enthält den Selbstaufruf.
Auf die Aufrufe bis zum Basisfall folgen die Rückgaben von innen nach außen.
Die Eingabeänderung muss die Abbruchbedingung nach endlich vielen Schritten erreichbar machen.
Zu viele offene Aufrufe können den Aufrufstapel einer Umsetzung überlaufen lassen.
Rekursion und Iteration berechnen bei der gezeigten Fakultät dasselbe Produkt; iterative Verfahren benötigen meist weniger Aufwand und Speicher.
Mit einer ausgedachten Funktion rechnen
- 1Die Funktion „sterne“ erlaubt ganze Eingaben ab null. Bei n=0 gibt sie 1 zurück, sonst berechnet sie 2+\operatorname{sterne}(n-1). Gesucht ist \operatorname{sterne}(3).
- 2Die Aufrufe haben nacheinander die Eingaben 3, 2, 1 und 0. Jeder der ersten drei Aufrufe wartet noch darauf, 2 zum inneren Ergebnis zu addieren.
- 3Der Basisfall \operatorname{sterne}(0) gibt 1 zurück.
- 4Damit gibt \operatorname{sterne}(1) den Wert 2+1=3 zurück.
- 5Danach gibt \operatorname{sterne}(2) den Wert 2+3=5 zurück.
- 6Schließlich gibt \operatorname{sterne}(3) den Wert 2+5=7 zurück. Das gesuchte Ergebnis ist 7.
Fehler finden
Fehler in Zeile 3Eine vorhandene Abbruchbedingung genügt nicht. Die Eingabe bleibt bei 5 und wird nicht null. Richtig: Die Abbruchbedingung wird bei diesem Start nicht erreicht.
Lückentext
Gibt eine Funktion für eine Eingabe direkt ein Ergebnis ohne weiteren Selbstaufruf zurück, ist dieser Fall der . Eine Funktion hat ihren Basisfall bei null und ruft sich sonst mit unveränderter Eingabe auf. Ein Start mit einer positiven Zahl führt daher . Wird die Fakultät mit einer Schleife statt mit Selbstaufrufen berechnet, heißt dieses Vorgehen .