web-dev-qa-db-de.com

rekursion versus Iteration

Ist es richtig zu sagen, dass überall, wo recursion verwendet wird, eine for-Schleife verwendet werden kann? Und wenn die Rekursion normalerweise langsamer ist, was ist der technische Grund dafür, sie jemals für die Schleifeniteration zu verwenden?

Und wenn es immer möglich ist, eine Rekursion in eine for-Schleife zu konvertieren, gibt es eine Faustregel?

83
Breako Breako

Die Rekursion ist normalerweise viel langsamer, da alle Funktionsaufrufe in einem Stapel gespeichert werden müssen, um zu den Anruferfunktionen zurückzukehren. In vielen Fällen muss Speicher zugewiesen und kopiert werden, um die Isolation des Bereichs zu implementieren.

Einige Optimierungen, z. B. Tail Call Optimization , beschleunigen Rekursionen, sind jedoch nicht immer möglich und nicht in allen Sprachen implementiert.

Die Hauptgründe für die Verwendung der Rekursion sind 

  • dass es in vielen Fällen intuitiver ist, wenn es unsere Herangehensweise an das Problem imitiert
  • dass einige Datenstrukturen wie Bäume einfacher durch Rekursion erforscht werden können (oder in jedem Fall Stacks benötigen würden)

Natürlich kann jede Rekursion can als eine Art Schleife modelliert werden: Das ist es, was die CPU letztendlich tun wird. Und die Rekursion selbst bedeutet direkter, die Funktionsaufrufe und Gültigkeitsbereiche in einen Stapel zu setzen. Wenn Sie jedoch Ihren rekursiven Algorithmus in einen Looping-Algorithmus umwandeln, ist möglicherweise viel Arbeit erforderlich, und der Code ist weniger wartbar: Wie bei jeder Optimierung sollte er nur versucht werden, wenn Profiling oder Beweise es als notwendig erweisen.

121
Denys Séguret

Ist es richtig zu sagen, dass überall, wo Rekursion verwendet wird, eine for-Schleife verwendet werden könnte?

Ja, weil die Rekursion in den meisten CPUs mit Schleifen und einer Stack-Datenstruktur modelliert wird.

Und wenn Rekursion normalerweise langsamer ist, was ist der technische Grund für die Verwendung?

Es ist nicht "normalerweise langsamer": Es ist eine falsch angewendete Rekursion, die langsamer ist. Außerdem können moderne Compiler einige Rekursionen ohne zu fragen in Loops umwandeln.

Und wenn es immer möglich ist, eine Rekursion in eine for-Schleife zu konvertieren, gibt es eine Faustregel?

Schreiben Sie iterative Programme für Algorithmen, die am besten verstanden werden, wenn Sie iterativ erklärt werden. schreiben rekursive Programme für Algorithmen, die am besten rekursiv erklärt werden.

Zum Beispiel wird das Suchen von Binärbäumen, das Ausführen von Quicksort und das Analysieren von Ausdrücken in vielen Programmiersprachen häufig rekursiv erklärt. Diese werden am besten auch rekursiv codiert. Auf der anderen Seite lassen sich die Berechnung von Fakultäten und die Berechnung der Fibonacci-Zahlen viel einfacher anhand von Iterationen 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 seine Sache wirklich gut macht+.


+ Ich habe mir die Vorschlaghammer-Analogie von Dijkstras "Programmdisziplin" geliehen.

46
dasblinkenlight

Frage:

Und wenn die Rekursion normalerweise langsamer ist, was ist der technische Grund dafür, sie jemals für die Schleifeniteration zu verwenden?

Antworten :

Denn in einigen Algorithmen ist es schwer iterativ zu lösen. Versuchen Sie, die Tiefensuche rekursiv und iterativ zu lösen. Sie werden auf die Idee kommen, dass es schwierig ist, DFS mit Iteration zu lösen.

Eine weitere gute Sache, die Sie ausprobieren können: Versuchen Sie, Merge sort iterativ zu schreiben. Dies wird einige Zeit in Anspruch nehmen.

Frage:

Ist es richtig zu sagen, dass überall, wo Rekursion verwendet wird, eine for-Schleife verwendet werden könnte?

Antworten :

Ja. Dieser Thread hat dafür eine sehr gute Antwort.

Frage:

Und wenn es immer möglich ist, eine Rekursion in eine for-Schleife zu konvertieren, gibt es eine Faustregel?

Antworten :

Vertrau mir. Versuchen Sie, Ihre eigene Version zu schreiben, um die Tiefensuche iterativ zu lösen. Sie werden feststellen, dass einige Probleme einfacher rekursiv zu lösen sind. 

Hinweis: Rekursion ist gut, wenn Sie ein Problem lösen, das mit divide and conquer technique gelöst werden kann.

22

Rekursion kann nicht nur langsamer sein, sondern auch zu Fehlern beim Stack-Überlauf führen, je nachdem, wie tief sie gehen.

4
G. Steigert

Um eine äquivalente Methode mit Iteration zu schreiben, müssen wir explizit einen Stack verwenden. Die Tatsache, dass die iterative Version einen Stack für ihre Lösung erfordert, zeigt an, dass das Problem schwer genug ist, um von Rekursion profitieren zu können. In der Regel ist Rekursion am besten für Probleme geeignet, die mit einer festen Menge an Speicher nicht gelöst werden können und folglich einen Stapel erfordern, wenn sie iterativ gelöst werden. Allerdings können Rekursion und Iteration dasselbe Ergebnis zeigen, wenn sie einem anderen Muster folgen .Um zu entscheiden, welche Methode besser funktioniert, ist von Fall zu Fall die beste Vorgehensweise, und zwar anhand des Musters, auf das das Problem folgt.

B. die n-te dreieckige Zahl von Dreiecksfolge ermitteln: 1 3 6 10 15… .__ Ein Programm, das einen iterativen Algorithmus zum Finden der n-ten dreieckigen Zahl verwendet:

Verwendung eines iterativen Algorithmus:

//Triangular.Java
import Java.util.*;
class Triangular {
   public static int iterativeTriangular(int n) {
      int sum = 0;
      for (int i = 1; i <= n; i ++)
         sum += i;
      return sum;
   }
   public static void main(String args[]) {
      Scanner stdin = new Scanner(System.in);
      System.out.print("Please enter a number: ");
      int n = stdin.nextInt();
      System.out.println("The " + n + "-th triangular number is: " + 
                            iterativeTriangular(n));
   }
}//enter code here

Verwendung eines rekursiven Algorithmus:

//Triangular.Java
import Java.util.*;
class Triangular {
   public static int recursiveTriangular(int n) {
      if (n == 1)
     return 1;  
      return recursiveTriangular(n-1) + n; 
   }

   public static void main(String args[]) {
      Scanner stdin = new Scanner(System.in);
      System.out.print("Please enter a number: ");
      int n = stdin.nextInt();
      System.out.println("The " + n + "-th triangular number is: " + 
                             recursiveTriangular(n)); 
   }
}
3
shirin

Die meisten Antworten scheinen davon auszugehen, dass iterative = for loop. Wenn Ihre for-Schleife nicht eingeschränkt ist (a la C, können Sie mit Ihrem Schleifenzähler tun, was Sie wollen), dann ist das richtig. Wenn es sich um eine realfor-Schleife handelt (z. B. in Python oder den meisten funktionalen Sprachen, in denen Sie den Schleifenzähler nicht manuell ändern können), ist er nicht korrekt. 

Alle (berechenbaren) Funktionen können sowohl rekursiv als auch mit while-Schleifen (oder bedingten Sprüngen, die im Grunde dasselbe sind) implementiert werden. Wenn Sie sich wirklich auf for loops beschränken, erhalten Sie nur eine Teilmenge dieser Funktionen (die primitiven rekursiven Funktionen, wenn Ihre elementaren Operationen sinnvoll sind). Zugegeben, es ist eine ziemlich große Untermenge, die jede einzelne Funktion enthält, die Sie in der Praxis wahrscheinlich entdecken werden.

Viel wichtiger ist jedoch, dass viele Funktionen sehr einfach rekursiv implementiert werden können und äußerst schwer iterativ zu implementieren sind (die manuelle Verwaltung des Aufrufstapels zählt nicht).

1
Jbeuh

Ich scheine mich zu erinnern, dass mein Informatikprofessor damals sagte, dass alle Probleme, die rekursive Lösungen haben, auch iterative Lösungen haben. Er sagt, dass eine rekursive Lösung normalerweise langsamer ist, sie wird jedoch häufig verwendet, wenn sie einfacher zu verstehen und zu codieren ist als iterative Lösungen.

Bei fortschrittlicheren rekursiven Lösungen glaube ich jedoch nicht, dass sie immer mit einer einfachen for-Schleife implementiert werden können.

Ja, wie sagte von Thanakron Tandavas ,

Rekursion ist gut, wenn Sie ein Problem lösen, das durch Divide- und Conquer-Technik gelöst werden kann.

Zum Beispiel: Türme von Hanoi

  1. N Ringe in aufsteigender Größe
  2. 3 Pole
  3. Ringe beginnen gestapelt auf Pol 1. Ziel ist es, die Ringe so zu bewegen, dass sie auf Pol 3 gestapelt sind.
    • Kann jeweils nur einen Ring bewegen. 
    • Kann einen größeren Ring nicht auf einen kleineren setzen.
  4. Iterative Lösung ist "mächtig und doch hässlich"; rekursive Lösung ist "elegant".
0
Ramesh Mukkera