2009-02-10 10 views
29

Soweit ich weiß, können am meisten rekursive Funktionen mit Schleifen umgeschrieben werden. Einige vielleicht härter als andere, aber die meisten von ihnen können neu geschrieben werden.Welche rekursiven Funktionen können nicht mit Loops umgeschrieben werden?

Unter welchen Bedingungen wird es unmöglich, eine rekursive Funktion mit einer Schleife neu zu schreiben (wenn solche Bedingungen bestehen)?

+0

Ich vermute, du meinst eigentlich, was nicht ohne irgendeine Form von Stapel neu geschrieben werden kann, ist das so? – AnthonyWJones

+0

Eigentlich nein. Ich meine, wenn es total unmöglich ist, es mit einer Schleife neu zu schreiben. Ich denke an indirekte Rekursion als Beispiel. –

+0

https://secweb.cs.odu.edu/~zeil/cs361/web/website/Lectures/recursionConversion/page/recursionConversion.html – user1709408

Antwort

32

Wenn Sie eine Funktion rekursiv verwenden, nimmt die Compiler Pflege für Sie von Stack-Management, das ist, was Rekursion möglich macht. Alles, was Sie rekursiv tun können, können Sie tun, indem Sie einen Stack selbst verwalten (für die indirekte Rekursion müssen Sie nur sicherstellen, dass Ihre verschiedenen Funktionen diesen Stack teilen).

Also, nein, es gibt nichts, was mit Rekursion getan werden kann, und das kann nicht mit eine Schleife und ein Stapel getan werden.

+1

Ich habe eine verwandte Frage: können alle rekursiven Funktionen als ** einzelne Schleife ** dargestellt werden? –

+2

@Abhinav: Es tut mir leid, auf einen sehr alten Thread zu antworten, aber dies ist mir aufgefallen und es gibt einen einfachen Beweis, dass die Antwort ja lautet: Eine Turing-Maschine macht alles, indem sie eine einzige Schleife ausführt: 1. eine Anweisung holen, 2. werten Sie es aus, 3. gehen Sie zu 1. – mokus

+0

@mokus Ihr Beweis scheint unvollständig. Ziel ist es zu beweisen, dass jede rekursive Funktion als eine einzige Schleife dargestellt werden kann.Sie sagen, dass ein TM in einer einzelnen Schleife ausgeführt wird. Wo kommt Rekursion dazu? –

4

Sie können alle als iterative Schleife geschrieben werden (aber einige benötigen möglicherweise noch einen Stack, um den vorherigen Zustand für spätere Iterationen beizubehalten).

1

Sie können immer eine Schleife verwenden, aber Sie müssen möglicherweise mehr Datenstruktur erstellen (z. B. einen Stapel simulieren).

5

Es ist nicht so wichtig, dass sie nicht mit Loops implementiert werden können, es ist die Tatsache, dass die Funktionsweise des rekursiven Algorithmus viel klarer und präziser (und in vielen Fällen mathematisch beweisbar) ist richtig.

Viele rekursive Funktionen können als rekursive Endlosschleife geschrieben werden, die zu einer Schleife optimiert werden kann, aber dies hängt sowohl vom Algorithmus als auch von der verwendeten Sprache ab.

8

Ich weiß nicht, über Beispiele, bei denen rekursive Funktionen können nicht auf eine iterative Version umgewandelt werden, aber unpraktisch oder extrem ineffizient Beispiele sind:

  • Baum-Traversal

  • schnelle Fourier-

  • quicksorts (und einige andere iirc)

Im Grunde alles, wo Sie beginnen müssen, grenzenlose potenzielle Zustände zu verfolgen.

15

Jede rekursive Funktion kann zur Iteration (in eine Schleife) gemacht werden, aber Sie müssen einen Stack selbst verwenden, um den Status beizubehalten.

Normalerweise Endrekursion ist einfach in eine Schleife zu konvertieren:

A(x) { 
    if x<0 return 0; 
    return something(x) + A(x-1) 
} 

in übersetzt werden kann:

A(x) { 
    temp = 0; 
    for i in 0..x { 
    temp = temp + something(i); 
    } 
    return temp; 
} 

Andere Arten von Rekursion, die in Endrekursion übersetzt werden können, sind auch leicht zu Veränderung. Die anderen erfordern mehr Arbeit.

wie folgt zusammen:

treeSum(tree) { 
    if tree=nil then 0 
    else tree.value + treeSum(tree.left) + treeSum(tree.right); 
} 

Ist das nicht leicht zu übersetzen. Sie können ein Teil der Rekursion entfernen, aber das andere ist nicht ohne eine Struktur möglich, um den Zustand zu halten.

treeSum(tree) { 
    walk = tree; 
    temp = 0; 
    while walk != nil { 
    temp = temp + walk.value + treeSum(walk.right); 
    walk = walk.left; 
    } 
} 
+2

Ihr ursprüngliches Tail-Recursive-Beispiel ist nicht ganz tail-rekursiv (zeigt aber immer noch den Punkt diese "lineare" Rekursion ist oft leicht zu übersetzen, während höhere Aritäten oft nicht so einfach sind. – Brian

+0

Danke. Das letzte Beispiel scheint das zu sein, wonach ich suche. Ist es wirklich unmöglich, Rekursion daraus zu entfernen? –

+1

Nein, Sie können es immer mit Schleifen umschreiben. Es ist fast mechanisch, in Code zu transformieren, der Fortsetzungen verwendet, die in Schleifen (nicht den Stapel) in einer Sprache wie F # kompiliert werden können, siehe z.B. http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!256.entry – Brian

2

Ein Beispiel, das sehr schwierig sein würde, von rekursiv zu konvertieren wäre zu iterativ die Ackermann function.

alt text

+2

Schönes Beispiel. Aber eine Frage bleibt: Ist es unmöglich oder nur extrem schwierig? –

+0

Nicht einmal zu schwierig, wenn Sie die allgemeinen Techniken kennen. – Brian

+2

Ich habe versucht, dies zu tun, und es scheint mir nicht schwierig. Überprüfen Sie diesen Code (und bitte sagen Sie mir nichts falsch): ... –

2

Indirekte Rekursion ist immer noch möglich, um in eine nicht-rekursive Schleife zu konvertieren; Beginnen Sie einfach mit einer der Funktionen und verknüpfen Sie die Aufrufe mit den anderen, bis Sie eine direkt rekursive Funktion haben, die dann in eine Schleife umgesetzt werden kann, die eine Stack-Struktur verwendet.

9

Jede rekursive Funktion kann mit einer einzigen Schleife implementiert werden.

Denken Sie nur, was ein Prozessor tut, führt er Anweisungen in einer einzigen Schleife aus.

+0

Eigentlich funktioniert es nicht als Schleife. Die Pipeline in einer modernen CPU ist viel mehr wie eine Montagelinie. Beginne bei der ersten Anweisung, gehe zur nächsten Anweisung auf dem Befehlszeiger ++. Einige Befehle modifizieren den Befehlszeiger selbst, was zu einer Schleife oder einem Sprung führt. – Spence

+1

@Spence Der Befehlszeiger ist nur Daten. – starblue

+0

Es ist ein bisschen mehr als nur Daten. Der größte Teil des Verzweigungsvorhersage-Cachespeichers läuft auf der Grundlage des Zeigers von der Position und früheren zukünftigen Anweisungen ab. Obwohl es durch Assembly modifiziert werden kann, ist es ein grundlegender Teil des Prozessors. – Spence

2

In SICP fordern die Autoren den Leser heraus, eine rein iterative Methode zur Implementierung des Problems "Zähländerung" (here's an example eins vom Projekt Euler) zu entwickeln.

Aber die strikte Antwort auf Ihre Frage wurde bereits gegeben - Schleifen und Stapel können jede Rekursion implementieren.

Verwandte Themen