Wie der Titel erklärt, habe ich eine sehr grundlegende Programmierfrage, die ich bisher noch nicht beantworten konnte. Herausfiltern aller (äußerst cleveren) "Um die Rekursion zu verstehen, müssen Sie zuerst die Rekursion verstehen." Antworten aus verschiedenen Online-Threads Ich verstehe es immer noch nicht ganz.
Wenn ich verstehe, dass wir, wenn wir nicht wissen, was wir nicht wissen, dazu neigen können, die falschen Fragen zu stellen oder die richtigen Fragen falsch zu stellen, werde ich mitteilen, was ich "denke". Meine Frage ist in der Hoffnung, dass jemand mit einer ähnlichen Einstellung einige teilen kann Ein bisschen Wissen, das mir hilft, die rekursive Glühbirne einzuschalten!
Hier ist die Funktion (die Syntax ist in Swift geschrieben):
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a: a + 1, b: b)
}
}
Wir werden 2 und 5 als unsere Argumente verwenden:
println(sumInts(a: 2, b: 5))
Die Antwort lautet natürlich 14. Aber mir ist nicht klar, wie dieser Wert erreicht wird.
Dies sind meine 2 Aufhängevorgänge:
Die Funktion wird rekursiv aufgerufen, bis eine Bedingung erfüllt ist. Diese Bedingung ist a> b. Wenn diese Bedingung erfüllt ist, geben Sie 0 zurück. Auf den ersten Blick würde ich erwarten, dass der Rückgabewert 0 ist, was offensichtlich falsch ist.
Das Ausdrucken des Wertes von 'a' bei jeder Iteration ergibt einen Wert, den ich erwarten würde: 2, 3, 4, 5 (an welchem Punkt 5 + 1> b, der die erste Bedingung erfüllt: a> b), aber ich nicht ' Ich sehe nicht, wie der Wert 14 erreicht wird.
Mein erster Gedanke ist, dass etwas Ähnliches wie das Magische geschieht:
var answer = a;
answer += a+1 until a > b;
return answer;
Wenn ich also Magie ausschließe, bekomme ich einfach nichts. Ich würde gerne verstehen, was mehr als nur implizit passiert.
Wenn jemand freundlich erklären könnte, was technisch während dieser Art von Funktion passiert und warum das Ergebnis nicht 0 ist und wie a + sumInts(a: a + 1, b: b) = 14
ich letztendlich für immer in Ihrer Schuld stehen würde.
quelle
LearnYouARecursion
, komplette Problemstellungen von Weltklasse-Professor!Antworten:
Ich denke, die Verwirrung rührt daher, dass man es als "dieselbe Funktion" betrachtet, die oft aufgerufen wird. Wenn Sie sich vorstellen, dass "viele Kopien derselben Funktion aufgerufen werden", ist dies möglicherweise klarer:
Nur eine Kopie der Funktion gibt jemals 0 zurück, und es ist nicht die erste (es ist die letzte). Das Ergebnis des Aufrufs des ersten ist also nicht 0.
Für die zweite Verwirrung denke ich, dass es einfacher sein wird, die Rekursion auf Englisch zu formulieren. Lesen Sie diese Zeile:
als "den Wert von 'a' plus zurückgeben (der Rückgabewert einer anderen Kopie der Funktion, der der Wert der Kopie von 'a' plus ist (der Rückgabewert einer anderen Kopie der Funktion, der der Wert der zweiten Kopie von 'ist). a 'plus (... ", wobei jede Kopie der Funktion eine neue Kopie von sich selbst mit einer um 1 erhöhten erzeugt, bis die Bedingung a> b erfüllt ist.
Wenn Sie die Bedingung a> b erreichen, die wahr ist, haben Sie einen (möglicherweise willkürlich) langen Stapel von Kopien der Funktion, die sich gerade in der Ausführung befinden und alle auf das Ergebnis der nächsten Kopie warten, um herauszufinden, was sie sind sollte zu 'a' hinzufügen.
(Bearbeiten: Außerdem ist zu beachten, dass der Stapel von Kopien der von mir erwähnten Funktion eine echte Sache ist, die echten Speicher beansprucht und Ihr Programm zum Absturz bringt, wenn es zu groß wird. Der Compiler kann es in einigen Fällen optimieren Fälle, aber die Erschöpfung des Stapelspeichers ist eine signifikante und unglückliche Einschränkung rekursiver Funktionen in vielen Sprachen.
quelle
a
undb
.Folgendes
sumInts(2,5)
würde der Computercomputer denken, wenn er in der Lage wäre:Wie Sie sehen, gibt ein Aufruf der Funktion
sumInts
tatsächlich 0 zurück, dies ist jedoch nicht der endgültige Wert, da der Computer noch 5 zu dieser 0, dann 4 zum Ergebnis, dann 3 und dann 2 hinzufügen muss, wie in den vier letzten Sätzen von beschrieben die Gedanken unseres Computers. Beachten Sie, dass der Computer bei der Rekursion nicht nur den rekursiven Aufruf berechnen muss, sondern sich auch merken muss, was mit dem vom rekursiven Aufruf zurückgegebenen Wert zu tun ist. Es gibt einen speziellen Bereich im Computerspeicher, den Stapel, in dem diese Art von Informationen gespeichert wird. Dieser Speicherplatz ist begrenzt und zu rekursive Funktionen können den Stapel erschöpfen: Dies ist der Stapelüberlauf , der unserer beliebtesten Website ihren Namen gibt.Ihre Aussage scheint implizit davon auszugehen, dass der Computer bei einem rekursiven Aufruf vergisst, was er war, aber dies ist nicht der Fall. Deshalb stimmt Ihre Schlussfolgerung nicht mit Ihrer Beobachtung überein.
Dies liegt daran, dass der Rückgabewert kein
a
Selbst ist, sondern die Summe aus dem Werta
und dem Wert, der vom rekursiven Aufruf zurückgegeben wird.quelle
sumInts
so ändern , dass die „Computergedanken“ tatsächlich aufgeschrieben werden. Sobald Sie eine Hand solcher Funktionen geschrieben haben, werden Sie es wahrscheinlich "verstanden" haben!Um die Rekursion zu verstehen, müssen Sie das Problem anders betrachten. Anstelle einer großen logischen Folge von Schritten, die als Ganzes Sinn macht, nehmen Sie stattdessen ein großes Problem und teilen sich in kleinere Probleme auf und lösen diese. Sobald Sie eine Antwort auf die Unterprobleme haben, kombinieren Sie die Ergebnisse der Unterprobleme, um das zu ergeben Lösung für das größere Problem. Denken Sie an Sie und Ihre Freunde, die die Anzahl der Murmeln in einem riesigen Eimer zählen müssen. Sie nehmen jeweils einen kleineren Eimer und zählen diese einzeln. Wenn Sie fertig sind, addieren Sie die Summen. Nun, wenn jeder von Ihnen einen Freund findet und die Eimer weiter aufteilt, müssen Sie nur noch auf diese anderen Freunde warten Finde ihre Summen heraus, bring sie jedem von dir zurück, du addierst sie. Und so weiter.
Sie müssen sich daran erinnern, dass die Funktion jedes Mal, wenn sie sich rekursiv aufruft, einen neuen Kontext mit einer Teilmenge des Problems erstellt. Sobald dieser Teil behoben ist, wird er zurückgegeben, damit die vorherige Iteration abgeschlossen werden kann.
Lassen Sie mich Ihnen die Schritte zeigen:
Sobald sumInts (a: 6, b: 5) ausgeführt wurde, können die Ergebnisse berechnet werden. Gehen Sie also die Kette mit den Ergebnissen zurück, die Sie erhalten:
Eine andere Möglichkeit, die Struktur der Rekursion darzustellen:
quelle
Rekursion ist ein schwieriges Thema, und ich glaube nicht, dass ich es hier voll und ganz gerecht werden kann. Stattdessen werde ich versuchen, mich auf den bestimmten Code zu konzentrieren, den Sie hier haben, und sowohl die Intuition für die Funktionsweise der Lösung als auch die Mechanik der Berechnung des Ergebnisses durch den Code zu beschreiben.
Der Code, den Sie hier angegeben haben, löst das folgende Problem: Sie möchten die Summe aller Ganzzahlen von a bis b einschließlich kennen. Für Ihr Beispiel möchten Sie die Summe der Zahlen von 2 bis einschließlich 5, einschließlich
Wenn Sie versuchen, ein Problem rekursiv zu lösen, sollte einer der ersten Schritte darin bestehen, herauszufinden, wie das Problem in ein kleineres Problem mit derselben Struktur zerlegt werden kann. Nehmen wir also an, Sie wollten die Zahlen von 2 bis einschließlich 5 zusammenfassen. Eine Möglichkeit, dies zu vereinfachen, besteht darin, festzustellen, dass die obige Summe als umgeschrieben werden kann
Hier ist (3 + 4 + 5) zufällig die Summe aller ganzen Zahlen zwischen 3 und 5 einschließlich. Mit anderen Worten, wenn Sie die Summe aller Ganzzahlen zwischen 2 und 5 wissen möchten, berechnen Sie zunächst die Summe aller Ganzzahlen zwischen 3 und 5 und addieren Sie dann 2.
Wie berechnet man also die Summe aller ganzen Zahlen zwischen 3 und 5 einschließlich? Nun, diese Summe ist
was stattdessen als gedacht werden kann
Hier ist (4 + 5) die Summe aller ganzen Zahlen zwischen 4 und 5 einschließlich. Wenn Sie also die Summe aller Zahlen zwischen 3 und 5 einschließlich berechnen möchten, berechnen Sie die Summe aller ganzen Zahlen zwischen 4 und 5 und addieren Sie dann 3.
Hier gibt es ein Muster! Wenn Sie die Summe der ganzen Zahlen zwischen a und b einschließlich berechnen möchten, können Sie Folgendes tun. Berechnen Sie zunächst die Summe der ganzen Zahlen zwischen a + 1 und b einschließlich. Fügen Sie als Nächstes ein zu dieser Summe hinzu. Sie werden feststellen, dass "Berechnen Sie die Summe der ganzen Zahlen zwischen a + 1 und b einschließlich" so ziemlich das gleiche Problem ist, das wir bereits zu lösen versuchen, aber mit leicht unterschiedlichen Parametern. Anstatt von a nach b einschließlich zu berechnen, berechnen wir von a + 1 nach b einschließlich. Das ist der rekursive Schritt - um das größere Problem zu lösen ("Summe von a nach b, einschließlich"), reduzieren wir das Problem auf eine kleinere Version von sich selbst ("Summe von a + 1 bis einschließlich b").
Wenn Sie sich den Code oben ansehen, werden Sie feststellen, dass dieser Schritt darin enthalten ist:
Dieser Code ist einfach eine Übersetzung der obigen Logik. Wenn Sie von a nach b einschließlich summieren möchten, summieren Sie zunächst a + 1 bis einschließlich b (das ist der rekursive Aufruf von
sumInt
s) und fügen Sie dann hinzua
.Natürlich wird dieser Ansatz an sich nicht funktionieren. Wie würden Sie beispielsweise die Summe aller ganzen Zahlen zwischen 5 und einschließlich 5 berechnen? Nun, mit unserer aktuellen Logik würden Sie die Summe aller ganzen Zahlen zwischen 6 und einschließlich 5 berechnen und dann 5 addieren. Wie berechnen Sie also die Summe aller ganzen Zahlen zwischen einschließlich 6 und 5? Nun, mit unserer aktuellen Logik würden Sie die Summe aller ganzen Zahlen zwischen 7 und 5 einschließlich berechnen und dann 6 hinzufügen. Sie werden hier ein Problem bemerken - das geht einfach weiter und weiter!
Bei der rekursiven Problemlösung muss es eine Möglichkeit geben, die Vereinfachung des Problems zu beenden und es stattdessen direkt zu lösen. In der Regel finden Sie einen einfachen Fall, in dem die Antwort sofort ermittelt werden kann, und strukturieren dann Ihre Lösung, um einfache Fälle direkt zu lösen, wenn sie auftreten. Dies wird normalerweise als Basisfall oder rekursive Basis bezeichnet .
Was ist der Grund für dieses spezielle Problem? Wenn Sie ganze Zahlen von a bis einschließlich b aufsummieren und a größer als b ist, lautet die Antwort 0 - es gibt keine Zahlen im Bereich! Daher strukturieren wir unsere Lösung wie folgt:
Vergleichen Sie nun diesen Pseudocode mit Ihrem tatsächlichen Code:
Beachten Sie, dass zwischen der im Pseudocode beschriebenen Lösung und diesem tatsächlichen Code fast genau eine Eins-zu-Eins-Zuordnung besteht. Der erste Schritt ist der Basisfall. Wenn Sie nach der Summe eines leeren Zahlenbereichs fragen, erhalten Sie 0. Andernfalls berechnen Sie die Summe zwischen a + 1 und b und fügen Sie dann a hinzu.
Bisher habe ich nur eine allgemeine Idee hinter dem Code gegeben. Aber Sie hatten zwei andere, sehr gute Fragen. Erstens, warum gibt dies nicht immer 0 zurück, da die Funktion sagt, dass 0 zurückgegeben werden soll, wenn a> b? Zweitens, woher kommen die 14 eigentlich? Schauen wir uns diese der Reihe nach an.
Versuchen wir einen sehr, sehr einfachen Fall. Was passiert, wenn Sie anrufen
sumInts(6, 5)
? In diesem Fall sehen Sie beim Durchlaufen des Codes, dass die Funktion nur 0 zurückgibt. Das ist das Richtige, um - es gibt keine Zahlen im Bereich. Versuchen Sie es jetzt noch einmal. Was passiert, wenn Sie anrufensumInts(5, 5)
? Nun, hier ist was passiert:sumInts(5, 5)
. Wir fallen in denelse
Zweig, der den Wert von `a + sumInts (6, 5) zurückgibt.sumInts(5, 5)
festzustellen, wassumInts(6, 5)
ist, müssen wir pausieren, was wir tun, und einen Anruf tätigensumInts(6, 5)
.sumInts(6, 5)
wird gerufen. Es betritt denif
Zweig und kehrt zurück0
. Diese Instanz vonsumInts
wurde jedoch von aufgerufensumInts(5, 5)
, sodass der Rückgabewert ansumInts(5, 5)
und nicht an den Anrufer der obersten Ebene zurückgemeldet wird.sumInts(5, 5)
Jetzt kann man rechnen5 + sumInts(6, 5)
, um zurück zu kommen5
. Es gibt es dann an den Anrufer der obersten Ebene zurück.Beachten Sie, wie der Wert 5 hier gebildet wurde. Wir begannen mit einem aktiven Anruf bei
sumInts
. Dadurch wurde ein weiterer rekursiver Aufruf ausgelöst, und der von diesem Aufruf zurückgegebene Wert übermittelte die Informationen ansumInts(5, 5)
. Der Aufruf vonsumInts(5, 5)
dann führte wiederum einige Berechnungen durch und gab einen Wert an den Anrufer zurück.Wenn Sie dies mit versuchen
sumInts(4, 5)
, geschieht Folgendes:sumInts(4, 5)
versucht zurückzukehren4 + sumInts(5, 5)
. Dazu ruft es aufsumInts(5, 5)
.sumInts(5, 5)
versucht zurückzukehren5 + sumInts(6, 5)
. Dazu ruft es aufsumInts(6, 5)
.sumInts(6, 5)
gibt 0 zurück zusumInts(5, 5).</li> <li>
sumInts (5, 5)now has a value for
sumInts (6, 5), namely 0. It then returns
5 + 0 = 5`.sumInts(4, 5)
hat jetzt einen Wert fürsumInts(5, 5)
, nämlich 5. Es kehrt dann zurück4 + 5 = 9
.Mit anderen Worten, der zurückgegebene Wert wird gebildet, indem die Werte einzeln aufsummiert werden, wobei jedes Mal ein Wert verwendet wird, der von einem bestimmten rekursiven Aufruf an zurückgegeben wird
sumInts
und der aktuelle Wert von addiert wirda
. Wenn die Rekursion ihren Tiefpunkt erreicht, gibt der tiefste Aufruf 0 zurück. Dieser Wert verlässt die rekursive Aufrufkette jedoch nicht sofort. Stattdessen wird der Wert nur eine Ebene darüber an den rekursiven Aufruf zurückgegeben. Auf diese Weise fügt jeder rekursive Aufruf nur eine weitere Zahl hinzu und gibt sie weiter oben in der Kette zurück, was mit der Gesamtsumme gipfelt. Versuchen Sie als Übung, dies aufzuspürensumInts(2, 5)
, womit Sie beginnen wollten.Hoffe das hilft!
quelle
Sie haben hier bisher einige gute Antworten, aber ich werde noch eine hinzufügen, die einen anderen Ansatz verfolgt.
Zunächst habe ich viele Artikel über einfache rekursive Algorithmen geschrieben, die Sie vielleicht interessant finden. sehen
http://ericlippert.com/tag/recursion/
http://blogs.msdn.com/b/ericlippert/archive/tags/recursion/
Diese befinden sich in der neuesten Reihenfolge. Beginnen Sie also von unten.
Zweitens haben bisher alle Antworten die rekursive Semantik unter Berücksichtigung der Funktionsaktivierung beschrieben . Jeder Aufruf führt eine neue Aktivierung durch , und der rekursive Aufruf wird im Rahmen dieser Aktivierung ausgeführt. Das ist eine gute Art, darüber nachzudenken, aber es gibt eine andere, äquivalente Art: intelligentes Text-Suchen und Ersetzen .
Lassen Sie mich Ihre Funktion in eine etwas kompaktere Form umschreiben. Betrachten Sie dies nicht als in einer bestimmten Sprache.
Ich hoffe das ergibt Sinn. Wenn Sie mit dem bedingten Operator nicht vertraut sind, hat er die Form
condition ? consequence : alternative
und seine Bedeutung wird klar.Jetzt sind wir bewerten wollen
s(2,5)
wir tun, indem Sie einen Text mit der Funktion Körper des Anrufs ersetzt tun, ersetzen Sie danna
mit2
undb
mit5
:Bewerten Sie nun die Bedingung. Wir ersetzen textlich
2 > 5
mitfalse
.Ersetzen Sie nun alle falschen Bedingungen in Textform durch die Alternative und alle wahren Bedingungen mit der Konsequenz. Wir haben nur falsche Bedingungen, daher ersetzen wir diesen Ausdruck textlich durch die Alternative:
+
Um zu vermeiden, dass ich all diese Zeichen eingeben muss, ersetzen Sie die konstante Arithmetik in Textform durch ihren Wert. (Dies ist ein kleiner Betrug, aber ich möchte nicht alle Klammern im Auge behalten müssen!)Suchen und ersetzen Sie nun, diesmal mit dem Textkörper für den Anruf,
3
füra
und5
für b. Wir werden den Ersatz für den Anruf in Klammern setzen:Und jetzt machen wir einfach weiter die gleichen Schritte zur Textersetzung:
Alles, was wir hier getan haben, war nur eine einfache Textersetzung . Eigentlich hätte ich "2 + 1" und so weiter nicht durch "3" ersetzen sollen, bis ich musste, aber pädagogisch wäre es schwer zu lesen geworden.
Die Funktionsaktivierung ist nichts anderes als das Ersetzen des Funktionsaufrufs durch den Hauptteil des Aufrufs und das Ersetzen der formalen Parameter durch die entsprechenden Argumente. Sie müssen vorsichtig sein, wenn Sie Klammern intelligent einfügen, aber abgesehen davon handelt es sich nur um Textersetzung.
Natürlich implementieren die meisten Sprachen die Aktivierung nicht als Textersetzung, sondern logisch das, was es ist.
Was ist dann eine unbegrenzte Rekursion? Eine Rekursion, bei der die Textersetzung nicht aufhört! Beachten Sie, wie wir schließlich zu einem Schritt kamen, bei dem es keinen
s
Ersatz mehr gab, und wir dann einfach die Regeln für die Arithmetik anwenden konnten.quelle
Die Art und Weise, wie ich normalerweise herausfinde, wie eine rekursive Funktion funktioniert, besteht darin, den Basisfall zu betrachten und rückwärts zu arbeiten. Hier ist diese Technik, die auf diese Funktion angewendet wird.
Zuerst der Basisfall:
Dann der Aufruf direkt darüber im Aufrufstapel :
Dann der Aufruf direkt darüber im Aufrufstapel:
Und so weiter:
Und so weiter:
Beachten Sie, dass wir zu unserem ursprünglichen Aufruf der Funktion gelangt sind
sumInts(2, 5) == 14
Die Reihenfolge, in der diese Aufrufe ausgeführt werden:
Die Reihenfolge, in der diese Anrufe zurückgegeben werden:
Beachten Sie, dass wir zu einer Schlussfolgerung über die Funktionsweise der Funktion gekommen sind, indem wir die Aufrufe in der Reihenfolge verfolgt haben, in der sie zurückgegeben werden .
quelle
Ich werde es versuchen.
Wenn ich die Gleichung a + sumInts (a + 1, b) ausführe, werde ich zeigen, wie die endgültige Antwort 14 ist.
Lassen Sie uns wissen, wenn Sie weitere Fragen haben.
Hier ist ein weiteres Beispiel für rekursive Funktionen im folgenden Beispiel.
Ein Mann hat gerade das College abgeschlossen.
t ist die Zeit in Jahren.
Die tatsächliche Gesamtzahl der Jahre vor der Pensionierung kann wie folgt berechnet werden:
Und das sollte gerade genug sein, um jemanden zu deprimieren, lol. ;-P
quelle
Rekursion. In der Informatik wird die Rekursion unter dem Thema Endliche Automaten ausführlich behandelt.
In seiner einfachsten Form ist es eine Selbstreferenz. Zum Beispiel ist die Aussage "Mein Auto ist ein Auto" eine rekursive Aussage. Das Problem ist, dass die Anweisung eine unendliche Rekursion ist, da sie niemals enden wird. Die Definition in der Aussage eines "Autos" ist, dass es ein "Auto" ist, so dass es ersetzt werden kann. Es gibt jedoch kein Ende, denn im Falle einer Substitution wird es immer noch "Mein Auto ist ein Auto".
Dies könnte anders sein, wenn die Aussage lautet: "Mein Auto ist ein Bentley. Mein Auto ist blau." In diesem Fall könnte der Ersatz in der zweiten Situation für das Auto "Bentley" sein, was zu "Mein Bentley ist blau" führt. Diese Arten von Substitutionen werden in der Informatik durch kontextfreie Grammatiken mathematisch erklärt .
Die eigentliche Substitution ist eine Produktionsregel. Da die Aussage durch S dargestellt wird und das Auto eine Variable ist, die ein "Bentley" sein kann, kann diese Aussage rekursiv rekonstruiert werden.
Dies kann auf verschiedene Arten konstruiert werden, da jedes
|
bedeutet, dass es eine Wahl gibt.S
kann durch eine dieser Auswahlmöglichkeiten ersetzt werden, und S beginnt immer leer. Dieε
Mittel, um die Produktion zu beenden. So wieS
es ersetzt werden kann, können auch andere Variablen ersetzt werden (es gibt nur eine und dieseC
würde "bentley" darstellen).Beginnen Sie also damit
S
, leer zu sein und es durch die erste Wahl"my"S
S
zu ersetzenS
kann weiterhin ersetzt werden, da es eine Variable darstellt. Wir könnten wieder "mein" wählen oder ε, um es zu beenden, aber lassen Sie uns weiterhin unsere ursprüngliche Aussage machen. Wir wählen den Raum, derS
durch ersetzt wird" "S
Als nächstes wählen wir C.
Und C hat nur eine Wahl für den Austausch
Und der Platz wieder für S.
Und so weiter
"my bentley is"S
,"my bentley is "S
,"my bentley is blue"S
,"my bentley is blue"
( als Ersatz für S für ε beendet die Produktion) und wir haben rekursiv unsere Aussage „meine bentley ist blau“ gebaut.Stellen Sie sich Rekursion als diese Produktionen und Ersetzungen vor. Jeder Schritt im Prozess ersetzt seinen Vorgänger, um das Endergebnis zu erzielen. Im genauen Beispiel der rekursiven Summe von 2 bis 5 erhalten Sie die Produktion
Das wird
quelle
Ich denke, der beste Weg, um rekursive Funktionen zu verstehen, besteht darin, zu erkennen, dass sie rekursive Datenstrukturen verarbeiten. Aber in Ihrer ursprünglichen Funktion
sumInts(a: Int, b: Int)
, die die Summe der Zahlen vona
bis rekursiv berechnetb
, scheint es sich jedoch nicht um eine rekursive Datenstruktur zu handeln. Versuchen wir eine leicht modifizierte Version,sumInts(a: Int, n: Int)
in der angegebenn
ist, wie viele Zahlen Sie hinzufügen.Jetzt ist sumInts rekursiv
n
, eine natürliche Zahl. Immer noch keine rekursiven Daten, oder? Nun, eine natürliche Zahl könnte unter Verwendung von Peano-Axiomen als rekursive Datenstruktur betrachtet werden:Also, 0 = Null, 1 = Nachfolger (Null), 2 = Nachfolger (Nachfolger (Null)) und so weiter.
Sobald Sie eine rekursive Datenstruktur haben, haben Sie die Vorlage für die Funktion. Für jeden nicht rekursiven Fall können Sie den Wert direkt berechnen. Für die rekursiven Fälle nehmen Sie an, dass die rekursive Funktion bereits funktioniert, und verwenden sie, um den Fall zu berechnen, aber das Argument zu dekonstruieren. Im Fall von Natural bedeutet dies, dass
Succesor(n)
wir anstelle vonn
oder gleichwertig anstelle vonn
verwendenn - 1
.Jetzt ist die rekursive Funktion einfacher zu programmieren. Zunächst der Basisfall,
n=0
. Was sollen wir zurückgeben, wenn wir keine Zahlen hinzufügen möchten? Die Antwort ist natürlich 0.Was ist mit dem rekursiven Fall? Wenn wir
n
Zahlen hinzufügen möchten, die mit beginnen,a
und wir bereits eine funktionierendesumInts
Funktion haben, die funktioniertn-1
? Nun, wir müssen hinzufügen ,a
und rufen Sie dannsumInts
mita + 1
, so dass wir am Ende mit:Das Schöne ist, dass Sie jetzt nicht mehr in der niedrigen Rekursionsstufe denken müssen. Sie müssen nur Folgendes überprüfen:
quelle
Sie könnten an der Implementierung von Funktionen durch Nisan und Schocken interessiert sein . Das verlinkte PDF ist Teil eines kostenlosen Online-Kurses. Es beschreibt den zweiten Teil einer Implementierung einer virtuellen Maschine, in dem der Schüler einen Compiler für die Sprache einer virtuellen Maschine zu einer Maschinensprache schreiben sollte. Die von ihnen vorgeschlagene Funktionsimplementierung kann rekursiv ausgeführt werden, da sie stapelbasiert ist.
So führen Sie in die Funktionsimplementierung ein: Beachten Sie den folgenden Code der virtuellen Maschine:
Wenn Swift in diese Sprache der virtuellen Maschine kompiliert wurde, dann der folgende Swift-Codeblock:
würde bis zu kompilieren
Die Sprache der virtuellen Maschine basiert auf einem globalen Stapel .
push constant n
schiebt eine Ganzzahl auf diesen globalen Stapel.Nach dem Ausführen der Zeilen 1 und 2 sieht der Stapel wie folgt aus:
256
und257
sind Speicheradressen.call mult
schiebt die Rückleitungsnummer (3) auf den Stapel und reserviert Platz für die lokalen Variablen der Funktion.... und es geht zum Etikett
function mult
. Der darin enthaltene Codemult
wird ausgeführt. Als Ergebnis der Ausführung dieses Codes berechnen wir das Produkt aus 2 und 3, das in der 0. lokalen Variablen der Funktion gespeichert ist.Kurz bevor
return
Sie von mult kommen, werden Sie die Zeile bemerken:Wir werden das Produkt auf den Stapel schieben.
Wenn wir zurückkehren, passiert Folgendes:
Nach der Rückkehr sind wir bereit, Zeile 4 auszuführen, und unser Stapel sieht folgendermaßen aus:
Jetzt schieben wir 4 auf den Stapel.
sub
ist eine primitive Funktion der Sprache der virtuellen Maschine. Es akzeptiert zwei Argumente und gibt das Ergebnis in der üblichen Adresse zurück: der des 0. Arguments.Jetzt haben wir
Nachdem Sie nun wissen, wie ein Funktionsaufruf funktioniert, ist es relativ einfach zu verstehen, wie die Rekursion funktioniert. Keine Magie , nur ein Stapel.
Ich habe Ihre
sumInts
Funktion in dieser Sprache der virtuellen Maschine implementiert:Jetzt werde ich es nennen:
Der Code wird ausgeführt und wir gelangen bis zum Haltepunkt, an dem wir
lte
zurückkehrenfalse
. So sieht der Stapel an dieser Stelle aus:Lassen Sie uns nun unsere Rekursion "abwickeln".
return
0 und gehe zu Zeile 15 und gehe weiter.Zeile 16:
add
Zeile 17:
return
5 und gehe zu Zeile 15 und gehe weiter.Zeile 16:
add
Zeile 17:
return
9 und gehe zu Zeile 15 und gehe weiter.Zeile 16:
add
Linie 17:
return
12 und gehe zu Linie 15 und gehe weiter.Zeile 16:
add
Linie 17:
return
14 und gehe zu Linie 21 und gehe weiter.Hier hast du es. Rekursion: Verherrlicht
goto
.quelle
Ein wirklich guter Tipp, den ich beim Lernen und Verstehen von Rekursion gefunden habe, ist, einige Zeit damit zu verbringen, eine Sprache zu lernen, die keine andere Form von Schleifenkonstruktion als Rekursion hat. Auf diese Weise erhalten Sie ein gutes Gefühl dafür, wie Sie Rekursion durch Übung verwenden können.
Ich folgte http://www.htdp.org/, das nicht nur ein Schema-Tutorial ist, sondern auch eine großartige Einführung in das Entwerfen von Programmen in Bezug auf Architektur und Design bietet.
Aber im Grunde müssen Sie etwas Zeit investieren. Ohne ein "festes" Verständnis der Rekursion erscheinen Ihnen bestimmte Algorithmen, wie z. B. das Zurückverfolgen, immer "hart" oder sogar "magisch". Also durchhalten. :-D
Ich hoffe das hilft und viel Glück!
quelle
Es gibt bereits viele gute Antworten. Trotzdem versuche ich es.
Beim Aufruf erhält eine Funktion einen zugewiesenen Speicherplatz , der auf dem Speicherplatz der Aufruferfunktion gestapelt ist . In diesem Speicherbereich behält die Funktion die an sie übergebenen Parameter, die Variablen und ihre Werte bei. Dieser Speicherplatz verschwindet zusammen mit dem endenden Rückruf der Funktion. Mit der Idee des Stapels wird nun der Speicherplatz der Aufruferfunktion aktiv.
Bei rekursiven Aufrufen werden mit derselben Funktion mehrere Speicherplätze übereinander gestapelt. Das ist alles. Die einfache Vorstellung, wie der Stapel im Speicher eines Computers funktioniert, sollte Sie durch die Vorstellung bringen, wie die Rekursion bei der Implementierung erfolgt.
quelle
Ein bisschen abseits des Themas, ich weiß, aber ... versuchen Sie, die Rekursion in Google nachzuschlagen ... Sie werden anhand eines Beispiels sehen, was es bedeutet :-)
Frühere Versionen von Google haben den folgenden Text zurückgegeben (aus dem Speicher zitiert):
Am 10. September 2014 wurde der Witz über die Rekursion aktualisiert:
Eine weitere Antwort finden Sie in dieser Antwort .
quelle
Stellen Sie sich Rekursion als mehrere Klone vor , die dasselbe tun ...
Sie bitten, [1] zu klonen: "Summenzahlen zwischen 2 und 5"
und voilá !!
quelle
Viele der obigen Antworten sind sehr gut. Eine nützliche Technik zum Lösen von Rekursionen besteht jedoch darin, zuerst zu formulieren, was wir tun möchten, und zu codieren, wie es ein Mensch lösen würde. Im obigen Fall wollen wir eine Folge aufeinanderfolgender Ganzzahlen (unter Verwendung der Zahlen von oben) zusammenfassen:
Beachten Sie nun, dass diese Zeilen verwirrend sind (nicht falsch, aber verwirrend).
Warum der Test
a>b
? Und warumreturn 0
Lassen Sie uns den Code ändern, um genauer zu reflektieren, was ein Mensch tut
Können wir es noch menschlicher machen? Ja! Normalerweise summieren wir von links nach rechts (2 + 3 + ...). Die obige Rekursion summiert sich jedoch von rechts nach links (... + 4 + 5). Ändern Sie den Code, um ihn wiederzugeben (Das
-
kann ein wenig einschüchternd sein, aber nicht viel)Einige mögen diese Funktion verwirrender finden, da wir am "fernen" Ende beginnen, aber das Üben kann dazu führen, dass sie sich natürlich anfühlt (und es ist eine weitere gute "Denk" -Technik: Versuchen Sie "beide" Seiten, wenn Sie eine Rekursion lösen). Und wieder spiegelt die Funktion wider, was ein Mensch (am meisten?) Tut: Nimmt die Summe aller linken Ganzzahlen und addiert die 'nächste' rechte Ganzzahl.
quelle
Es fiel mir schwer, die Rekursion zu verstehen, dann fand ich diesen Blog und ich sah diese Frage bereits, also dachte ich, ich muss sie teilen. Sie müssen diesen Blog lesen. Ich fand das äußerst hilfreich. Es erklärt mit Stack und erklärt sogar, wie zwei Rekursionen mit Stack Schritt für Schritt funktionieren. Ich empfehle Ihnen, zuerst zu verstehen, wie der Stapel funktioniert, was hier sehr gut erklärt wird: Reise zum Stapel
then now you will understand how recursion works now take a look of this post
: Rekursion Schritt für Schritt verstehenEs ist ein Programm:
quelle
Rekursion machte für mich Sinn, als ich aufhörte zu lesen, was andere darüber sagen, oder es als etwas betrachtete, das ich vermeiden kann, und einfach Code schrieb. Ich habe ein Problem mit einer Lösung gefunden und versucht, die Lösung zu duplizieren, ohne zu suchen. Ich habe mir die Lösung erst angesehen, als ich hilflos feststeckte. Dann habe ich wieder versucht, es zu duplizieren. Ich habe dies bei mehreren Problemen erneut durchgeführt, bis ich mein eigenes Verständnis und Gespür dafür entwickelt habe, wie man ein rekursives Problem identifiziert und löst. Als ich dieses Level erreicht hatte, fing ich an, Probleme zu erfinden und zu lösen. Das hat mir mehr geholfen. Manchmal können Dinge nur gelernt werden, indem man sie selbst ausprobiert und kämpft. bis Sie es "bekommen".
quelle
Lassen Sie mich anhand eines Beispiels der Fibonacci-Serie sagen, Fibonacci ist
so lassen Sie sehen , wie Rekursion funktioniert, ersetze ich nur
n
int(n)
mitn-1
und so weiter. es sieht aus:wir wissen , ob
t(0)=(n-k)
Gleichen1
dannn-k=0
son=k
ersetzen wirk
mitn
:wenn wir
n-n
dann weglassen :so
3+2+1+(n-1)+n
ist die natürliche Zahl. es berechnet alsΣ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2
das ergebnis für fib ist:
O(1 + n²) = O(n²)
Dies ist der beste Weg, um die rekursive Beziehung zu verstehen
quelle