Ist es richtig zu sagen, dass überall dort, wo Rekursion verwendet wird, eine for
Schleife verwendet werden könnte? Und wenn die Rekursion normalerweise langsamer ist, was ist der technische Grund dafür, sie jemals über eine for
Schleifeniteration zu verwenden?
Und wenn es immer möglich ist, eine Rekursion in eine for
Schleife umzuwandeln, gibt es eine Faustregel dafür?
recursion
vsiteration
?iteration = for loop
Meiner Ansicht nach.Antworten:
Die Rekursion ist normalerweise viel langsamer, da alle Funktionsaufrufe in einem Stapel gespeichert werden müssen, damit die Aufruferfunktionen wieder aufgerufen werden können. In vielen Fällen muss Speicher zugewiesen und kopiert werden, um die Bereichsisolierung zu implementieren.
Einige Optimierungen, wie die Tail-Call-Optimierung , beschleunigen Rekursionen, sind jedoch nicht immer möglich und nicht in allen Sprachen implementiert.
Die Hauptgründe für die Verwendung der Rekursion sind
Natürlich jede Rekursion kann als eine Art Schleife modelliert werden: das ist , was die CPU letztlich tun wird. Und die Rekursion selbst bedeutet direkter, die Funktionsaufrufe und Bereiche in einem Stapel abzulegen. Das Ändern Ihres rekursiven Algorithmus in einen Schleifenalgorithmus erfordert jedoch möglicherweise viel Arbeit und macht Ihren Code weniger wartbar: Wie bei jeder Optimierung sollte nur versucht werden, wenn eine Profilerstellung oder ein Beweis dies als notwendig erweist.
quelle
f(n)
, die die n-te Fibonacci-Zahl zurückgibt .Ja, da die Rekursion in den meisten CPUs mit Schleifen und einer Stapeldatenstruktur modelliert wird.
Es ist nicht "normalerweise langsamer": Es ist eine falsch angewendete Rekursion, die langsamer ist. Darüber hinaus können moderne Compiler einige Rekursionen gut in Schleifen konvertieren, ohne danach zu fragen.
Schreiben Sie iterative Programme für Algorithmen, die am besten verstanden werden, wenn sie iterativ erklärt werden. Schreiben Sie rekursive Programme für Algorithmen, die am besten rekursiv erklärt werden.
Beispielsweise wird das Durchsuchen von Binärbäumen, das Ausführen von Quicksort und das Parsen von Ausdrücken in vielen Programmiersprachen häufig rekursiv erläutert. Diese werden am besten auch rekursiv codiert. Andererseits sind das Berechnen von Fakultäten und das Berechnen von Fibonacci-Zahlen durch Iterationen viel einfacher zu erklären. Rekursion für sie zu verwenden ist wie Fliegen mit einem Vorschlaghammer zu schlagen: Es ist keine gute Idee, selbst wenn der Vorschlaghammer wirklich gute Arbeit leistet + .
+ Ich habe mir die Vorschlaghammer-Analogie aus Dijkstras "Disziplin der Programmierung" ausgeliehen.
quelle
Frage:
Antworten :
Weil es in einigen Algorithmen schwierig ist, es iterativ zu lösen. Versuchen Sie, die Tiefensuche sowohl rekursiv als auch iterativ zu lösen. Sie werden auf die Idee kommen, dass es einfach schwierig ist, DFS mit Iteration zu lösen.
Eine weitere gute Sache zum Ausprobieren: Versuchen Sie, Merge sort iterativ zu schreiben. Es wird einige Zeit dauern.
Frage:
Antworten :
Ja. Dieser Thread hat eine sehr gute Antwort darauf.
Frage:
Antworten :
Vertrau mir. Versuchen Sie, Ihre eigene Version zu schreiben, um die Tiefensuche iterativ zu lösen. Sie werden feststellen, dass einige Probleme leichter rekursiv zu lösen sind.
Tipp: Rekursion ist gut, wenn Sie ein Problem lösen, das durch Teilen und Erobern gelöst werden kann.
quelle
Die Rekursion ist nicht nur langsamer, sondern kann auch zu Stapelüberlauffehlern führen, je nachdem, wie tief sie reicht.
quelle
Um eine äquivalente Methode mit Iteration zu schreiben, müssen wir explizit einen Stapel verwenden. Die Tatsache, dass die iterative Version einen Stapel für ihre Lösung benötigt, zeigt, dass das Problem so schwierig ist, dass es von einer Rekursion profitieren kann. In der Regel eignet sich die Rekursion am besten für Probleme, die nicht mit einer festen Speichermenge gelöst werden können und daher bei iterativer Lösung einen Stapel erfordern. Rekursion und Iteration können jedoch dasselbe Ergebnis zeigen, während sie unterschiedlichen Mustern folgen. Um zu entscheiden, welche Methode besser funktioniert, müssen Sie von Fall zu Fall entscheiden.
So finden Sie beispielsweise die n-te Dreieckszahl der Dreiecksfolge: 1 3 6 10 15… Ein Programm, das einen iterativen Algorithmus verwendet, um die n-te Dreieckszahl zu ermitteln:
Verwenden eines iterativen Algorithmus:
Verwenden eines rekursiven Algorithmus:
quelle
Die meisten Antworten scheinen davon auszugehen, dass
iterative
=for loop
. Wenn Ihre for-Schleife nicht eingeschränkt ist ( a la C, Sie können mit Ihrem Schleifenzähler alles tun, was Sie wollen), ist das richtig. Wenn es sich um eine echtefor
Schleife handelt (z. B. in Python oder den meisten funktionalen Sprachen, in denen Sie den Schleifenzähler nicht manuell ändern können), ist sie nicht korrekt.Alle (berechenbaren) Funktionen können sowohl rekursiv als auch mithilfe von
while
Schleifen (oder bedingten Sprüngen, die im Grunde dasselbe sind) implementiert werden. Wenn Sie sich wirklich darauf beschränkenfor loops
, erhalten Sie nur eine Teilmenge dieser Funktionen (die primitiven rekursiven, wenn Ihre elementaren Operationen sinnvoll sind). Zugegeben, es ist eine ziemlich große Teilmenge, die zufällig jede einzelne Funktion enthält, auf die Sie in der Praxis wahrscheinlich stoßen werden.Viel wichtiger ist, dass viele Funktionen sehr einfach rekursiv und schrecklich schwer iterativ zu implementieren sind (die manuelle Verwaltung Ihres Aufrufstapels zählt nicht).
quelle
Ja, wie gesagt durch Thanakron Tandavas ,
Zum Beispiel: Türme von Hanoi
quelle
Ich erinnere mich an meinen Informatikprofessor, der damals sagte, dass alle Probleme mit rekursiven Lösungen auch iterative Lösungen haben. Er sagt, dass eine rekursive Lösung normalerweise langsamer ist, aber sie werden häufig verwendet, wenn sie leichter zu überlegen und zu codieren sind als iterative Lösungen.
Bei fortgeschritteneren rekursiven Lösungen glaube ich jedoch nicht, dass sie immer mit einer einfachen
for
Schleife implementiert werden können .quelle
Rekursion + Auswendiglernen könnte zu einer effizienteren Lösung führen als ein rein iterativer Ansatz. Überprüfen Sie dies beispielsweise: http://jsperf.com/fibonacci-memoized-vs-iterative-for-large-n
quelle
Kurze Antwort: Der Kompromiss ist, dass die Rekursion schneller ist und für Schleifen in fast allen Fällen weniger Speicherplatz benötigt. Normalerweise gibt es jedoch Möglichkeiten, die for-Schleife oder die Rekursion zu ändern, damit sie schneller ausgeführt wird
quelle