Wie würde man bei einer willkürlich doppelt rekursiven Funktion ihre Laufzeit berechnen?
Zum Beispiel (im Pseudocode):
int a(int x){
if (x < = 0)
return 1010;
else
return b(x-1) + a(x-1);
}
int b(int y){
if (y <= -5)
return -2;
else
return b(a(y-1));
}
Oder so ähnlich.
Mit welchen Methoden könnte oder sollte man so etwas bestimmen?
Antworten:
Sie ändern immer wieder Ihre Funktion. Aber suchen Sie sich weiterhin solche aus, die für immer ohne Konvertierung auskommen.
Rekursion wird kompliziert, schnell. Der erste Schritt zur Analyse einer vorgeschlagenen doppelt rekursiven Funktion besteht darin, sie anhand einiger Beispielwerte zu verfolgen, um zu sehen, was sie bewirkt. Wenn Ihre Berechnung in eine Endlosschleife gerät, ist die Funktion nicht genau definiert. Wenn Ihre Berechnung in eine Spirale gerät, die immer größere Zahlen enthält (was sehr leicht vorkommt), ist sie wahrscheinlich nicht genau definiert.
Wenn Sie eine Antwort finden, versuchen Sie, ein Muster oder eine Wiederholungsbeziehung zwischen den Antworten zu finden. Sobald Sie das haben, können Sie versuchen, seine Laufzeit herauszufinden. Das herauszufinden kann sehr, sehr kompliziert sein, aber wir haben Ergebnisse wie den Hauptsatz , mit denen wir die Antwort in vielen Fällen herausfinden können.
Beachten Sie, dass es auch bei einer einzelnen Rekursion leicht ist, Funktionen zu finden, deren Laufzeit wir nicht berechnen können. Betrachten Sie zum Beispiel Folgendes:
Es ist derzeit nicht bekannt, ob diese Funktion immer genau definiert ist, geschweige denn, wie lange sie ausgeführt wird.
quelle
Die Laufzeit dieses bestimmten Funktionspaares ist unendlich, da keine der beiden Funktionen zurückkehrt, ohne die andere aufzurufen. Der Rückgabewert
a
ist immer abhängig von dem Rückgabewert eines Anruf zub
dem immer rufta
... und das ist , was als bekannt ist unendliche Rekursion .quelle
a
ruft nur an,b
wenn die übergebene Nummer> = 0 ist. Aber ja, es gibt eine Endlosschleife.Die naheliegende Methode besteht darin, die Funktion auszuführen und zu messen, wie lange es dauert. Hier erfahren Sie jedoch nur, wie lange eine bestimmte Eingabe dauert. Und wenn Sie vorher nicht wissen, dass die Funktion beendet wird, gibt es keine mechanische Möglichkeit, um herauszufinden, ob die Funktion beendet wird - das ist das Problem , das zum Stillstand kommt , , und es ist nicht zu entscheiden.
Das Ermitteln der Laufzeit einer Funktion ist nach dem Satz von Rice in ähnlicher Weise unentscheidbar . Tatsächlich zeigt der Satz von Rice, dass sogar entschieden wird, ob eine Funktion ausgeführt wird
O(f(n))
Zeit treffen ist.Das Beste, was Sie im Allgemeinen tun können, ist, Ihre menschliche Intelligenz (die unseres Wissens nicht an die Grenzen von Turing-Maschinen gebunden ist) zu nutzen und zu versuchen, ein Muster zu erkennen oder ein solches zu erfinden. Eine typische Methode zum Analysieren der Laufzeit einer Funktion besteht darin, die rekursive Definition der Funktion in eine rekursive Gleichung für ihre Laufzeit umzuwandeln (oder einen Satz von Gleichungen für wechselseitig rekursive Funktionen):
Was nun? Sie haben jetzt ein mathematisches Problem: Sie müssen diese Funktionsgleichungen lösen. Ein Ansatz, der häufig funktioniert, besteht darin, diese Gleichungen für ganzzahlige Funktionen in Gleichungen für analytische Funktionen umzuwandeln und diese mithilfe von Kalkül zu lösen, die Funktionen zu interpretieren
T_a
und FunktionenT_b
zu erzeugen .Zur Erzeugung von Funktionen und anderen diskreten mathematischen Themen empfehle ich das Buch Concrete Mathematics von Ronald Graham, Donald Knuth und Oren Patashnik.
quelle
Wie andere betonten, kann die Analyse der Rekursion sehr schnell sehr schwierig werden. Hier ist ein weiteres Beispiel dafür: http://rosettacode.org/wiki/Mutual_recursion http://en.wikipedia.org/wiki/Hofstadter_sequence#Hofstadter_Female_and_Male_sequences Es ist schwierig, eine Antwort und eine Laufzeit für diese zu berechnen. Dies liegt daran, dass diese gegenseitig rekursiven Funktionen eine "schwierige Form" haben.
Wie auch immer, schauen wir uns dieses einfache Beispiel an:
http://pramode.net/clojure/2010/05/08/clojure-trampoline/
Beginnen wir mit dem Versuch, Folgendes zu berechnen
funa(m), m > 0
:Die Laufzeit beträgt:
Lassen Sie uns nun ein anderes, etwas komplizierteres Beispiel auswählen:
Inspiriert von http://planetmath.org/encyclopedia/MutualRecursion.html , was für sich genommen eine gute Lektüre ist, betrachten wir: "" Fibonacci-Zahlen können durch gegenseitige Rekursion interpretiert werden: F (0) = 1 und G (0) ) = 1 mit F (n + 1) = F (n) + G (n) und G (n + 1) = F (n).
Was ist die Laufzeit von F? Wir werden den anderen Weg gehen.
Nun, R (F (0)) = 1 = F (0); R (G (0)) = 1 = G (0)
Jetzt ist R (F (1)) = R (F (0)) + R (G (0)) = F (0) + G (0) = F (1)
...
Es ist nicht schwer zu erkennen, dass R (F (m)) = F (m) - z. B. ist die Anzahl der Funktionsaufrufe, die zum Berechnen einer Fibonacci-Zahl am Index i erforderlich sind, gleich dem Wert einer Fibonacci-Zahl am Index i. Dies setzt voraus, dass das Addieren von zwei Zahlen viel schneller ist als ein Funktionsaufruf. Wenn dies nicht der Fall wäre, dann wäre dies wahr: R (F (1)) = R (F (0)) + 1 + R (G (0)), und die Analyse davon wäre komplizierter gewesen, möglicherweise ohne eine einfache geschlossene Formlösung.
Die geschlossene Form der Fibonacci-Sequenz ist nicht unbedingt leicht neu zu erfinden, ganz zu schweigen von einigen komplizierteren Beispielen.
quelle
Zunächst muss gezeigt werden, dass die von Ihnen definierten Funktionen enden und für welche Werte genau. In dem von Ihnen definierten Beispiel
b
Kündigt nur für an,y <= -5
denn wenn Sie einen anderen Wert einstecken, haben Sie eine Laufzeit des Formularsb(a(y-1))
. Wenn Sie etwas weiter expandieren, werden Sie feststellen, dass ein Term der Formb(a(y-1))
schließlich zu dem Termb(1010)
führt, der zu einem Termb(a(1009))
führt, der wiederum zu dem Term führtb(1010)
. Dies bedeutet, dass Sie keinen Wert einfügen könnena
, der nicht erfüllt,x <= -4
da Sie am Ende eine Endlosschleife haben, in der der zu berechnende Wert vom zu berechnenden Wert abhängt. Dieses Beispiel hat also im Wesentlichen eine konstante Laufzeit.Die einfache Antwort lautet also, dass es keine allgemeine Methode zum Ermitteln der Laufzeit rekursiver Funktionen gibt, da es keine allgemeine Prozedur gibt, die bestimmt, ob eine rekursiv definierte Funktion beendet wird.
quelle
Laufzeit wie in Big-O?
Das ist ganz einfach: O (N) - vorausgesetzt, es liegt eine Beendigungsbedingung vor.
Rekursion ist nur eine Schleife, und eine einfache Schleife ist O (N), egal wie viele Dinge Sie in dieser Schleife tun (und das Aufrufen einer anderen Methode ist nur ein weiterer Schritt in der Schleife).
Interessant wird es, wenn Sie eine Schleife innerhalb einer oder mehrerer der rekursiven Methoden haben. In diesem Fall erhalten Sie eine Art exponentielle Leistung (multipliziert mit O (N) bei jedem Durchgang durch die Methode).
quelle
O(2^n)
undO(n*log(n))
jeweils.a
Anrufeb
undb
Anrufe ,a
so dass Sie können nicht einfach davon ausgehen , dass jede Methode Zeit in Anspruch nimmtO(1)
.