Praktische Informatik
Rekursion, Teile und herrsche, Rücksetzverfahren
Ein Verfahren, das sich selbst aufruft, und warum das kein Zirkelschluss ist, sondern ein Werkzeug.
Benötigte Grundlagen
Dieses Vorwissen brauchst du für das Kapitel. Schau kurz nach, wenn dir etwas davon nicht mehr präsent ist, sonst leg direkt los.
Einführung
„Um zu verstehen, was Rekursion ist, muss man zuerst verstehen, was Rekursion ist."
Der Witz trifft die Sache und verfehlt sie zugleich. Er trifft, weil sich eine rekursive Definition tatsächlich auf sich selbst beruft. Er verfehlt, weil ihm der entscheidende Teil fehlt: der Abbruch.
Ohne Abbruch wäre Rekursion ein Zirkelschluss. Mit Abbruch ist sie eines der mächtigsten Werkzeuge der Informatik, und sie löst Aufgaben, die mit kaum formulierbar wären.
Das kannst du nach diesem Kapitel
Basisfall und Rekursionsschritt unterscheiden und beide selbst aufstellen.
einen rekursiven Aufruf über den nachvollziehen.
erkennen, wann Rekursion einer überlegen ist und wann nicht.
das Prinzip Teile und herrsche auf ein neues Problem anwenden.
das Rücksetzverfahren an einem Beispiel erläutern (Vertiefung).
Kurz aufgefrischt
Vorausgesetzt werden mit Parametern und Rückgabewerten aus Programmieren: Variablen, Kontrollstrukturen, Unterprogramme, der aus Datentypen und Datenstrukturen und die aus Aufwand von Algorithmen.
Die zwei notwendigen Teile
Ein heißt rekursiv, wenn es sich selbst aufruft. Damit das kein endloser Rückgriff wird, braucht es zwingend zwei Teile:
Basisfall. Ein Fall, der ohne weiteren Aufruf beantwortet wird. Er ist der Boden. Rekursionsschritt. Der Aufruf auf ein kleineres Teilproblem, das dem Basisfall näher liegt.
Beide sind unverzichtbar. Fehlt der Basisfall, läuft die Rekursion endlos. Führt der Rekursionsschritt nicht näher an den Basisfall heran, ebenfalls.
Beispiel Fakultät. Die mathematische Definition ist bereits rekursiv:
fakultaet(n):
wenn n = 0:
gib 1 zurück // Basisfall
sonst:
gib n * fakultaet(n-1) // Rekursionsschritt
Die Umsetzung ist eine wörtliche Übersetzung der Definition, und genau das ist die Stärke der Rekursion: Wo ein Problem rekursiv definiert ist, ist die rekursive Lösung die natürliche.
Beide Teile, und warum keiner fehlen darf
Im Bild stecken genau die zwei Teile, ohne die keine Rekursion funktioniert. Der Seitenzweig ist der Basisfall: Er beantwortet die Frage ohne weiteren Aufruf und ist damit der Boden, auf dem alles aufsetzt. Der gerade Weg ist der Rekursionsschritt, und entscheidend ist an ihm nicht, dass er sich selbst aufruft, sondern dass er es mit tut, also mit einem Wert, der dem Basisfall näher liegt. Nimm einen der beiden weg, und das Bild zeigt sofort, was passiert: Ohne Seitenzweig gibt es keinen Ausgang, und ohne die Verkleinerung wird der Ausgang nie erreicht. Beides endet in derselben Endlosschleife. (Die Bedingung steht hier als , im Quelltext daneben als , dieselbe , nur von der anderen Seite gelesen.)
Wie der Rechner das abarbeitet
Bei jedem Aufruf legt der Rechner einen Eintrag auf den mit den lokalen Werten und der Stelle, an der es nach der Rückkehr weitergeht. Für :
fakultaet(4) → 4 * fakultaet(3) ↓ hinein
fakultaet(3) → 3 * fakultaet(2) ↓
fakultaet(2) → 2 * fakultaet(1) ↓
fakultaet(1) → 1 * fakultaet(0) ↓
fakultaet(0) → 1 ← Boden erreicht
fakultaet(1) = 1 * 1 = 1 ↑ heraus
fakultaet(2) = 2 * 1 = 2 ↑
fakultaet(3) = 3 * 2 = 6 ↑
fakultaet(4) = 4 * 6 = 24 ↑
🔴 Zwei Beobachtungen, die den größten Teil aller Missverständnisse ausräumen:
Erst geht es hinein, dann heraus. Die Multiplikationen finden nach dem tiefsten Aufruf statt, auf dem Rückweg. Wer glaubt, die Rechnung geschehe beim Hineingehen, kommt bei jeder Nachverfolgung durcheinander.
Der Stapel wächst mit der Tiefe. Jeder offene Aufruf belegt Speicher. Bei läuft der Aufrufstapel über, und das Programm bricht ab. Rekursionstiefe ist also nicht kostenlos, und das ist der wichtigste praktische Nachteil.
Wann Rekursion sinnvoll ist
Die Fakultät ist ein schlechtes Beispiel für den Nutzen der Rekursion, auch wenn sie ein gutes für ihr Prinzip ist. Eine leistet dasselbe, ist schneller und braucht keinen Stapelplatz.
Die Frage lautet deshalb: Wann lohnt sich Rekursion?
Wenn sich das Problem natürlich in gleichartige kleinere Teilprobleme zerlegt.
Typische Fälle:
- Bäume und Verzeichnisse. Ein Ordner enthält Ordner, die Ordner enthalten. Eine Schleife müsste die Schachtelungstiefe kennen, die Rekursion nicht.
- Teile und herrsche. Sortieren durch Mischen, binäre Suche.
- Alle Möglichkeiten durchprobieren. Labyrinth, Damenproblem, Sudoku.
In all diesen Fällen ist die rekursive Fassung kürzer und verständlicher. Bei einfachen Wiederholungen dagegen ist die Schleife die bessere Wahl.
Ein Beispiel, das ohne Rekursion mühsam wäre
Die Türme von Hanoi: Ein von Scheiben soll von Stab A nach C, es darf nur eine Scheibe auf einmal bewegt werden, und nie eine größere auf eine kleinere.
Die rekursive Lösung besteht aus drei Zeilen:
bewege(n, von, nach, hilf):
wenn n = 0: fertig // Basisfall
bewege(n-1, von, hilf, nach) // oberste n-1 auf den Hilfsstab
verschiebe die unterste Scheibe von → nach
bewege(n-1, hilf, nach, von) // die n-1 wieder obendrauf
Der Gedanke dahinter ist bemerkenswert: Man löst das Problem, ohne es ganz zu durchdenken. Man nimmt an, dass der Fall bereits gelöst ist, und beschreibt nur, wie man daraus den Fall gewinnt. Genau das ist die Denkweise der vollständigen Induktion.
Die Zahl der Züge ist . Bei 64 Scheiben, wie in der Legende, wären das über Züge; bei einem Zug pro Sekunde dauerte das länger als das bisherige Alter des Universums. Die Lösung ist also kurz und trotzdem exponentiell teuer.
Türme von Hanoi mit drei Scheiben
Jeder ist ein Aufruf, und jeder Aufruf bewegt genau eine Scheibe, die unterste seines . Die Beschriftung nennt, wie viele Scheiben er übernimmt und von welchem Stab zu welchem. Jetzt zähle: Der Baum hat sieben Knoten, also braucht die Lösung sieben Züge, und . Die Formel steht damit nicht als Behauptung daneben, sondern ist abzählbar. 🔴 Bemerkenswert ist, wie diese Lösung zustande kommt: Niemand hat sich sieben Züge ausgedacht. Man nimmt an, der Fall mit zwei Scheiben sei schon gelöst, und beschreibt nur, wie man daraus den Fall mit dreien gewinnt, obere zwei beiseite, unterste ans Ziel, obere zwei wieder darauf. Genau das ist die Denkweise der vollständigen Induktion. Bei 64 Scheiben ergäbe dieselbe Rechnung über Züge: Die Lösung ist kurz und trotzdem exponentiell teuer.
Wenn Rekursion teuer wird
Die Fibonacci-Folge lädt zur direkten Umsetzung ein:
fib(n):
wenn n ≤ 1: gib n zurück
gib fib(n-1) + fib(n-2)
Kurz, richtig und katastrophal langsam. Der Grund ist die Mehrfachberechnung:
fib(5)
/ \
fib(4) fib(3)
/ \ / \
fib(3) fib(2) fib(2) fib(1)
/ \
fib(2) fib(1)
wird zweimal berechnet, dreimal, und bei größerem wächst das exponentiell. Für wären es über Aufrufe.
Abhilfe: Zwischenergebnisse merken statt neu berechnen. Man nennt das Speichern bereits berechneter Werte, und damit sinkt der Aufwand von auf . Alternativ genügt eine einfache , die von unten hochrechnet.
🔴 Die Lehre daraus ist wichtig: Rekursiv formulierbar heißt nicht rekursiv sinnvoll. Prüfe immer, ob dieselben Teilprobleme mehrfach auftreten.
fib(5): jede Zahl ist ein Aufruf
Die Zahl im ist das des Aufrufs . Markiert ist alles, was mehr als einmal vorkommt, und das ist die Pointe: wird zweimal berechnet, dreimal und ebenfalls zweimal, sieben der neun Knoten sind reine Wiederholung. Jedes Mal entsteht derselbe Wert, und jedes Mal rechnet ihn das Programm von Grund auf neu aus. Bei ist das ärgerlich, bei vernichtend: Der Baum wächst exponentiell, und es wären über Aufrufe. 🔴 Die Abhilfe ist so einfach wie der Fehler: Zwischenergebnisse merken statt neu berechnen, dann fällt der Aufwand von auf . Die Lehre daraus gilt über dieses Beispiel hinaus: Rekursiv formulierbar heißt nicht rekursiv sinnvoll. Prüfe immer, ob dieselben Teilprobleme mehrfach auftreten, der Baum zeigt es sofort.
Vertiefung: Rücksetzverfahren
Dieser Abschnitt gehört zum erhöhten Anforderungsniveau.
Manche Aufgaben lassen sich nur durch Ausprobieren lösen, aber nicht durch blindes: Ein Labyrinth, ein Sudoku, die Frage, ob acht Damen auf ein Schachbrett passen, ohne sich zu bedrohen.
Das Rücksetzverfahren (Backtracking) geht so vor:
Wähle eine mögliche Fortsetzung. Prüfe, ob sie die Bedingungen noch erfüllen kann. Vertiefe rekursiv, wenn ja. Setze zurück und probiere die nächste Möglichkeit, wenn nein.
loese(teilloesung):
wenn teilloesung vollständig:
gib sie aus, fertig
für jede mögliche Fortsetzung:
wenn Fortsetzung zulässig:
füge sie hinzu
loese(teilloesung) // vertiefen
entferne sie wieder // zurücksetzen
Die Zeile „entferne sie wieder" ist der Kern und der Namensgeber. Sie stellt den vor dem Versuch wieder her, damit die nächste Möglichkeit auf sauberem Grund beginnt.
Beim Damenproblem setzt man Zeile für Zeile eine Dame. Steht sie schlecht, geht man in die vorige Zeile zurück und versucht dort die nächste Spalte.
Der entscheidende Gewinn liegt im frühen Abschneiden. Erkennt man in Zeile 3, dass keine Fortsetzung mehr möglich ist, spart man sich alle Anordnungen der übrigen Zeilen auf einen Schlag. Beim Acht-Damen-Problem gäbe es Millionen Stellungen; das Rücksetzverfahren prüft nur einen kleinen Bruchteil davon.
Wichtig ist aber, was das nicht bedeutet: Der Aufwand bleibt im schlechtesten Fall exponentiell. Das Rücksetzverfahren macht ein schweres Problem nicht leicht, es macht es nur oft handhabbar. Damit steht es genau an der Grenze, die im Kapitel über und beschrieben wurde.
Rücksetzverfahren: der Gewinn steckt im Abbruch
Zwei Versuche, die erste Dame zu setzen. Der linke führt in eine Sackgasse: In der nächsten Zeile ist keine Spalte mehr frei, die Teillösung kann also nicht mehr vollständig werden. Statt trotzdem weiterzuprobieren, nimmt das Verfahren die Dame zurück und geht zum nächsten Versuch, die Zeile „entferne sie wieder“ im Ablauf ist genau das und gibt dem Verfahren seinen Namen. 🔴 Und hier steckt der ganze Gewinn, aber er steckt in dem, was das Bild nicht zeigt: Unter der Sackgasse werden gar keine mehr erzeugt. Beim Acht-Damen-Problem gäbe es Millionen Stellungen; wer in Zeile 3 abbricht, erspart sich mit einem Schlag alle Anordnungen der fünf Zeilen darunter. Was das nicht heißt: Der schlechteste Fall bleibt exponentiell. Das Rücksetzverfahren macht ein schweres Problem nicht leicht, es macht es oft handhabbar, und steht damit genau an der Grenze, die im Kapitel über und beschrieben ist.
Einen rekursiven Aufruf verfolgen
Verfolge für das Verfahren: gibt 0 zurück, wenn , sonst .
- 1
Hineingehen, jeder Aufruf kommt auf den und wartet:
summe(4) = 4 + summe(3) ← wartet summe(3) = 3 + summe(2) ← wartet summe(2) = 2 + summe(1) ← wartet summe(1) = 1 + summe(0) ← wartet summe(0) = 0 ← Basisfall, gibt sofort zurück - 2
An dieser Stelle liegen fünf Aufrufe auf dem Stapel. Genau das ist der Speicherbedarf der Rekursion.
- 3
Herauskommen, nun werden die wartenden Additionen von unten nach oben ausgeführt:
summe(1) = 1 + 0 = 1 summe(2) = 2 + 1 = 3 summe(3) = 3 + 3 = 6 summe(4) = 4 + 6 = 10 - 4
Ergebnis: 10, und tatsächlich ist .
- 5
Die häufigste Fehlvorstellung: dass zuerst rechnet. Das kann nicht sein, denn bei steht der zweite Summand noch gar nicht fest. Der Aufruf muss erst vollständig zurückkehren.
- 6
Beobachtung zur Tiefe: Für liegen Aufrufe gleichzeitig auf dem Stapel. Bei reicht der Stapel nicht mehr, und das Programm bricht ab. Eine hätte diese Grenze nicht.
. Fünf Aufrufe auf dem Stapel, die Additionen erfolgen alle auf dem Rückweg.
Warum Fibonacci naiv scheitert
Zeige, warum die direkte rekursive Berechnung von exponentiell viele Aufrufe braucht, und wie man das behebt.
- 1
Aufrufbaum für zeichnen:
fib(5) / \ fib(4) fib(3) / \ / \ fib(3) fib(2) fib(2) fib(1) / \ fib(2) fib(1) - 2
Zählen, was mehrfach vorkommt: zweimal, dreimal. Und jeder dieser Zweige berechnet seinerseits alles darunter erneut.
- 3
Warum das exponentiell wird: Jeder Aufruf erzeugt zwei weitere. Der Aufrufbaum verdoppelt sich also näherungsweise mit jeder Ebene, und bei Ebenen stehen etwa da.
- 4
Für sind das über Aufrufe, also Stunden Rechenzeit für eine Zahl mit elf Stellen.
- 5
Abhilfe 1: Zwischenergebnisse merken. Man legt eine Tabelle an und prüft vor jeder Berechnung, ob der Wert schon darinsteht. Dann wird jedes genau einmal berechnet, und der Aufwand sinkt auf .
- 6
Abhilfe 2: von unten. Man rechnet der Reihe nach hoch und merkt sich nur die letzten beiden Werte. Ebenfalls , dazu ohne Stapelbedarf.
- 7
Die allgemeine Lehre: Rekursion ist dann teuer, wenn dieselben Teilprobleme mehrfach auftreten. Beim Sortieren durch Mischen passiert das nicht, denn dort sind die Hälften verschieden; deshalb bleibt es dort bei .
Der naive Aufruf ist wegen Mehrfachberechnung; mit gemerkten Zwischenwerten oder einer Schleife wird daraus .
Typischer Fehler
„Der Basisfall ist Formsache; Hauptsache, der rekursive Aufruf ist richtig."
Ohne Basisfall gibt es überhaupt kein Ergebnis, nur einen Absturz.
Sieh dir das an:
fakultaet(n):
gib n * fakultaet(n-1) zurück
Der Rekursionsschritt ist mathematisch völlig korrekt. Trotzdem läuft das Programm bis , , weiter und bricht mit einem Stapelüberlauf ab.
Ebenso wichtig ist die zweite Bedingung: Der Rekursionsschritt muss dem Basisfall näher kommen. Dieser hat einen Basisfall und läuft trotzdem endlos:
fakultaet(n):
wenn n = 0: gib 1 zurück
gib n * fakultaet(n) zurück // n statt n-1
Der Basisfall wird nie erreicht, weil sich das Argument nicht verändert.
Daher die Prüfliste, die man bei jeder Rekursion durchgehen sollte:
Gibt es einen Basisfall? Wird er von jedem Startwert aus erreicht? Wird das Argument bei jedem Schritt echt kleiner?
Bei gehört übrigens auch der negative Bereich dazu. Ein Basisfall hilft nicht, wenn jemand aufruft; sauber wäre oder eine ausdrückliche Prüfung der Eingabe.
Übung 1
leichta) Nenne die zwei notwendigen Teile jeder Rekursion. b) Was geschieht, wenn der Basisfall fehlt? c) Verfolge und gib alle Zwischenergebnisse an.
Tipp anzeigen
Zu c): Erst hinein bis zum Basisfall, dann heraus.
Lösung anzeigen
a) Ein Basisfall, der ohne weiteren Aufruf beantwortet wird, und ein Rekursionsschritt, der auf ein kleineres, dem Basisfall näher liegendes Teilproblem führt.
b) Die Aufrufe hören nicht auf. Jeder belegt Platz auf dem , und wenn dieser voll ist, bricht das Programm mit einem Stapelüberlauf ab.
c) Hineingehen: (Basisfall)
Herauskommen: , dann , dann .
Ergebnis: 6.
Detaillierte Schritterklärung anzeigen
Hier wird jeder Schritt einzeln erklärt, vor allem, warum er gemacht wird.
✦ Empfohlen: Standard – Die normale Erklärungstiefe passt zum Einstieg.
- 1
a) Basisfall und Rekursionsschritt, und warum beide nötig sind
Ein Basisfall, der ohne weiteren Aufruf beantwortet wird, und ein Rekursionsschritt, der auf ein kleineres, dem Basisfall näher liegendes Teilproblem führt. Beide Bedingungen zusammen garantieren, dass die Kette endet.
- 2
b) Was ohne Basisfall geschieht
Die Aufrufe hören nicht auf. Jeder belegt Platz auf dem Aufrufstapel, und wenn dieser voll ist, bricht das Programm mit einem Stapelüberlauf ab.
- 3
c) fakultaet(3) verfolgen: erst hinein, dann heraus
Hinein bis zum Basisfall: , , , (Basisfall). Heraus: , dann , dann . Ergebnis 6.
Zwischenergebnis
Übung 2
mittela) Schreibe ein rekursives Verfahren, das die Quersumme einer natürlichen Zahl berechnet. b) Verfolge es für die Zahl 472. c) Erkläre, warum die naive rekursive Berechnung von Fibonacci so teuer ist. d) Nenne zwei Möglichkeiten, das zu beheben, und gib jeweils den Aufwand an.
Tipp anzeigen
Zu a): Wie bekommt man die letzte Ziffer und wie den Rest?
Lösung anzeigen
a) Die letzte Ziffer ist der Rest bei Division durch 10, der Rest der Zahl ist die ganzzahlige Division durch 10.
quersumme(n):
wenn n = 0:
gib 0 zurück // Basisfall
gib (n mod 10) + quersumme(n ganzzahlig / 10) // Rekursionsschritt
Das Argument wird bei jedem Schritt um eine Stelle kürzer und erreicht damit sicher die 0.
b) Hineingehen:
Herauskommen: , dann , dann .
Ergebnis: 13, und stimmt.
c) Weil dieselben Teilprobleme mehrfach berechnet werden. ruft und auf, und beide berechnen ihrerseits überlappende Teilbäume neu. Jeder Aufruf erzeugt zwei weitere, der Aufrufbaum verdoppelt sich also näherungsweise je Ebene, was auf etwa Aufrufe führt. Für sind das über .
d) Zwischenergebnisse merken: Vor jeder Berechnung in einer Tabelle nachsehen, ob der Wert schon vorliegt. Jedes wird dann genau einmal berechnet → .
von unten: der Reihe nach hochrechnen und nur die letzten beiden Werte merken → , zusätzlich ohne Stapelbedarf und mit Speicher.
Detaillierte Schritterklärung anzeigen
Hier wird jeder Schritt einzeln erklärt, vor allem, warum er gemacht wird.
✦ Empfohlen: Standard – Die normale Erklärungstiefe passt zum Einstieg.
- 1
Teil a): Die Zerlegung finden
Man sucht, wie sich das Problem in ein kleineres desselben Typs zerlegt. Bei der Quersumme ist das die Trennung in letzte Ziffer und Rest.
472 = 2 + \text{Quersumme von } 47
Zwischenergebnis
liefert die Ziffer, den Rest.
Der Basisfall ist hier zwingend, denn nur er beendet den Abstieg. Und er wird sicher erreicht, weil die Zahl bei jedem Schritt eine Stelle verliert.
- 2
Teil b): Zwei Spalten führen
Man notiert erst alle Aufrufe beim Hineingehen und rechnet dann beim Herauskommen von unten nach oben zusammen.
Zwischenergebnis
.
- 3
Teil c): Den Aufrufbaum zeichnen
Statt zu behaupten, dass es langsam ist, zeichnet man den Baum und zählt, was mehrfach vorkommt. Bei tritt bereits dreimal auf.
\text{etwa } 2^n \text{ Aufrufe}
Zwischenergebnis
Jeder Aufruf erzeugt zwei weitere → exponentiell.
- 4
Teil d): Zwei Wege, ein Ergebnis
Beide Wege beseitigen dieselbe Ursache, nämlich die Mehrfachberechnung. Der eine merkt sich Ergebnisse, der andere rechnet gleich in der richtigen Reihenfolge.
Zwischenergebnis
Beide ; die Schleife zusätzlich ohne Stapelbedarf.
Wenn ein Problem sich so leicht von unten aufbauen lässt, ist die Schleife meist die bessere Wahl. Die Rekursion lohnt dort, wo die Reihenfolge der Teilprobleme nicht von vornherein feststeht, etwa bei Bäumen.
Übung 3
schwera) Erkläre die rekursive Lösung der Türme von Hanoi und begründe, warum sie Züge braucht. b) (Vertiefung) Beschreibe das Rücksetzverfahren am Damenproblem. c) (Vertiefung) Warum spart das Rücksetzverfahren gegenüber dem Durchprobieren aller Stellungen, und warum bleibt es trotzdem im schlechtesten Fall exponentiell? d) Nenne einen Fall, in dem eine der Rekursion vorzuziehen ist, und begründe.
Tipp anzeigen
Zu a): Wie oft wird das Verfahren für aufgerufen?
Lösung anzeigen
a) Lösung: Um Scheiben von A nach C zu bringen, verlegt man zuerst die obersten Scheiben von A nach B (rekursiv), schiebt dann die unterste Scheibe von A nach C und verlegt schließlich die Scheiben von B nach C (wieder rekursiv). Der Basisfall ist , dort ist nichts zu tun.
Zügezahl: Sei die Anzahl der Züge. Aus dem Ablauf folgt unmittelbar
Daraus ergibt sich , , , , allgemein . Man sieht es auch direkt: Jede zusätzliche Scheibe verdoppelt die Arbeit und fügt einen Zug hinzu.
b) Man setzt Zeile für Zeile eine Dame:
- In Zeile 1 die erste zulässige Spalte wählen.
- In Zeile 2 die erste Spalte suchen, die von keiner bereits gesetzten Dame bedroht wird (weder gleiche Spalte noch gleiche Diagonale).
- So weiter, bis alle acht Damen stehen. Dann ist eine Lösung gefunden.
- Gibt es in einer Zeile keine zulässige Spalte, geht man in die vorige Zeile zurück, entfernt die dortige Dame und versucht die nächste Spalte.
Das Zurückgehen ist der namensgebende Schritt. Es stellt den vor dem Versuch wieder her, damit die nächste Möglichkeit unverfälscht beginnt.
c) Ersparnis durch frühes Abschneiden: Wird schon in Zeile 3 erkannt, dass keine zulässige Fortsetzung existiert, entfallen alle Anordnungen der Zeilen 4 bis 8 auf einen Schlag. Beim blinden Durchprobieren wären das Stellungen allein an dieser einen Stelle. Statt der Millionen Gesamtstellungen prüft das Verfahren deshalb nur einen kleinen Bruchteil.
Warum trotzdem exponentiell: Das Abschneiden hängt davon ab, wie früh Widersprüche auftreten, und das ist eine Eigenschaft der Aufgabe, keine Garantie des Verfahrens. Im ungünstigsten Fall werden Widersprüche erst ganz unten erkannt, und dann bleibt fast der gesamte Suchbaum zu durchlaufen. Das Rücksetzverfahren macht ein schweres Problem also nicht leicht, sondern oft handhabbar. Damit steht es genau an der Grenze zwischen und : Eine gefundene Lösung ist schnell prüfbar, das Finden bleibt teuer.
d) Beispiel: die Summe aller Zahlen von 1 bis oder allgemein jede einfache Wiederholung fester Anzahl.
Begründung: Die Rekursion legt für jeden Schritt einen Eintrag auf den , verbraucht also Speicher und scheitert bei großem an einem Stapelüberlauf. Eine Schleife kommt mit Speicher aus und ist zusätzlich schneller, weil kein Aufrufaufwand entsteht. Da sich das Problem nicht natürlich in gleichartige Teilprobleme zerlegt, sondern nur eine Wiederholung darstellt, bringt die Rekursion hier auch keinen Gewinn an Verständlichkeit.
Die Faustregel lautet: Rekursion bei geschachtelten Strukturen, Schleife bei einfachen Wiederholungen.
Detaillierte Schritterklärung anzeigen
Hier wird jeder Schritt einzeln erklärt, vor allem, warum er gemacht wird.
✦ Empfohlen: Standard – Die normale Erklärungstiefe passt zum Einstieg.
- 1
a) Die rekursive Lösung: das Problem auf ein kleineres zurückführen
Um Scheiben von A nach C zu bringen: erst die obersten Scheiben von A nach B verlegen (rekursiv), dann die unterste Scheibe von A nach C schieben, dann die Scheiben von B nach C verlegen (wieder rekursiv). Basisfall ist . Dort ist nichts zu tun.
- 2
a) Die Zügezahl aus der Rekursionsgleichung
Sei die Anzahl der Züge. Aus dem Ablauf folgt unmittelbar und , zweimal die Teillösung plus ein Zug für die unterste Scheibe. Daraus: , , , , allgemein .
,
- 3
b) (Vertiefung) Das Rücksetzverfahren am Damenproblem
Man setzt Zeile für Zeile eine Dame: In Zeile 1 die erste zulässige Spalte wählen; in Zeile 2 die erste Spalte suchen, die von keiner bereits gesetzten Dame bedroht wird (weder gleiche Spalte noch gleiche Diagonale); so weiter bis alle acht stehen. Gibt es in einer Zeile keine zulässige Spalte, geht man in die vorige Zeile zurück, entfernt die dortige Dame und versucht die nächste Spalte.
- 4
c) (Vertiefung) Warum Backtracking spart, und trotzdem exponentiell bleibt
Ersparnis durch frühes Abschneiden: Wird schon in Zeile 3 erkannt, dass keine zulässige Fortsetzung existiert, entfallen alle Anordnungen der Zeilen 4 bis 8 auf einen Schlag, beim blinden Durchprobieren wären das Stellungen allein an dieser Stelle. Statt der Millionen Gesamtstellungen prüft das Verfahren nur einen kleinen Bruchteil.
- 5
d) Wann eine Schleife der Rekursion vorzuziehen ist
Beispiel: die Summe aller Zahlen von 1 bis , oder allgemein jede einfache Wiederholung fester Anzahl. Begründung: Die Rekursion legt für jeden Schritt einen Eintrag auf den Aufrufstapel, verbraucht also Speicher und scheitert bei großem an einem Stapelüberlauf. Eine Schleife kommt mit Speicher aus und ist zusätzlich schneller, weil kein Aufrufaufwand entsteht.
Zusammenfassung
Ein rekursives ruft sich selbst auf und braucht dafür zwingend zwei Teile: einen Basisfall, der ohne weiteren Aufruf beantwortet wird, und einen Rekursionsschritt, der dem Basisfall näher kommt; fehlt einer von beiden, endet die Rekursion im Stapelüberlauf. Der Rechner legt jeden offenen Aufruf auf den , weshalb erst vollständig hineingegangen und dann auf dem Rückweg gerechnet wird und die Rekursionstiefe durch den Speicher begrenzt ist. Sinnvoll ist Rekursion dort, wo ein Problem natürlich in gleichartige kleinere Teile zerfällt, etwa bei Verzeichnissen, bei Teile und herrsche oder beim Durchprobieren aller Möglichkeiten; bei einfachen Wiederholungen ist die schneller und sparsamer. Dass sich etwas rekursiv formulieren lässt, macht es nicht sinnvoll: Die naive Fibonacci-Berechnung ist exponentiell, weil sich die Teilprobleme überlappen, und wird durch gemerkte Zwischenergebnisse oder eine Schleife auf gebracht. Das Rücksetzverfahren schließlich probiert Möglichkeiten schrittweise durch und setzt bei Widersprüchen zurück; sein früher Abbruch spart oft gewaltig, hebt aber den im schlechtesten Fall exponentiellen Aufwand nicht auf.


