Ich verstehe die Big-O-Notation, weiß aber nicht, wie ich sie für viele Funktionen berechnen soll. Insbesondere habe ich versucht, die rechnerische Komplexität der naiven Version der Fibonacci-Sequenz herauszufinden:
int Fibonacci(int n)
{
if (n <= 1)
return n;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Was ist die rechnerische Komplexität der Fibonacci-Sequenz und wie wird sie berechnet?
Antworten:
Sie modellieren die zu berechnende Zeitfunktion
Fib(n)
als Summe der zu berechnenden ZeitFib(n-1)
plus der zu berechnenden ZeitFib(n-2)
plus der Zeit, um sie zu addieren (O(1)
). Dies setzt voraus, dass wiederholte Auswertungen derselbenFib(n)
dieselbe Zeit in Anspruch nehmen - dh es wird keine Memoisierung verwendet.T(n<=1) = O(1)
T(n) = T(n-1) + T(n-2) + O(1)
Sie lösen diese Wiederholungsbeziehung (z. B. mithilfe von Generierungsfunktionen) und erhalten die Antwort.
Alternativ können Sie den Rekursionsbaum zeichnen, der Tiefe hat
n
und intuitiv herausfindet, dass diese Funktion asymptotisch ist . Sie können dann Ihre Vermutung durch Induktion beweisen.O(2
n
)
Basis:
n = 1
ist offensichtlichEs sei angenommen , deshalb
T(n-1) = O(2
n-1
)
T(n) = T(n-1) + T(n-2) + O(1)
das ist gleichT(n) = O(2
n-1
) + O(2
n-2
) + O(1) = O(2
n
)
Wie in einem Kommentar erwähnt, ist dies jedoch nicht die enge Grenze. Eine interessante Tatsache über diese Funktion ist, dass T (n) asymptotisch der gleiche Wert ist wie der Wert von,
Fib(n)
da beide definiert sind alsf(n) = f(n-1) + f(n-2)
.Die Blätter des Rekursionsbaums geben immer 1 zurück. Der Wert von
Fib(n)
ist die Summe aller Werte, die von den Blättern im Rekursionsbaum zurückgegeben werden, was der Anzahl der Blätter entspricht. Da jedes Blatt O (1) zur Berechnung benötigt,T(n)
ist es gleichFib(n) x O(1)
. Folglich ist die enge Grenze für diese Funktion die Fibonacci-Sequenz selbst (~ ). Sie können diese enge Grenze herausfinden, indem Sie die oben erwähnten Generierungsfunktionen verwenden.θ(1.6
n
)
quelle
Fragen Sie sich einfach, wie viele Anweisungen ausgeführt werden müssen, um
F(n)
abgeschlossen zu werden.Denn
F(1)
die Antwort lautet1
(der erste Teil der Bedingung).Denn
F(n)
die Antwort lautetF(n-1) + F(n-2)
.Welche Funktion erfüllt diese Regeln? Versuchen Sie ein n (a> 1):
a n == a (n-1) + a (n-2)
Teilen Sie durch a (n-2) :
a 2 == a + 1
Löse nach
a
und du bekommst(1+sqrt(5))/2 = 1.6180339887
, auch bekannt als der goldene Schnitt .Es dauert also exponentiell.
quelle
Ich stimme pgaur und rickerbh zu, die Komplexität von rekursivem Fibonacci ist O (2 ^ n).
Ich bin durch eine eher vereinfachte, aber meiner Meinung nach immer noch gültige Argumentation zu dem gleichen Schluss gekommen.
Zunächst geht es darum herauszufinden, wie oft die rekursive Fibonacci-Funktion (F () von nun an) bei der Berechnung der N-ten Fibonacci-Zahl aufgerufen wird. Wenn es einmal pro Nummer in der Folge 0 bis n aufgerufen wird, dann haben wir O (n), wenn es für jede Nummer n-mal aufgerufen wird, erhalten wir O (n * n) oder O (n ^ 2), und so weiter.
Wenn also F () für eine Zahl n aufgerufen wird, wächst die Häufigkeit, mit der F () für eine gegebene Zahl zwischen 0 und n-1 aufgerufen wird, wenn wir uns 0 nähern.
Als ersten Eindruck scheint es mir, dass, wenn wir es visuell formulieren, das Zeichnen einer Einheit pro Mal, wenn F () für eine bestimmte Zahl aufgerufen wird, nass eine Art Pyramidenform erhält (dh wenn wir Einheiten horizontal zentrieren) ). Etwas wie das:
Die Frage ist nun, wie schnell sich die Basis dieser Pyramide vergrößert, wenn n wächst.
Nehmen wir einen realen Fall, zum Beispiel F (6)
Wir sehen, dass F (0) 32 Mal aufgerufen wird, was 2 ^ 5 ist, was für diesen Beispielfall 2 ^ (n-1) ist.
Nun wollen wir wissen, wie oft F (x) überhaupt aufgerufen wird, und wir können sehen, wie oft F (0) aufgerufen wird, ist nur ein Teil davon.
Wenn wir mental alle * von F (6) nach F (2) in die Linie F (1) verschieben, sehen wir, dass die Linien F (1) und F (0) jetzt gleich lang sind. Das heißt, die Gesamtzeit, zu der F () aufgerufen wird, wenn n = 6 2x32 = 64 = 2 ^ 6 ist.
Nun zur Komplexität:
quelle
Es gibt eine sehr schöne Diskussion über dieses spezielle Problem am MIT . Auf Seite 5 wird darauf hingewiesen, dass die für die Berechnung von Fib (N) erforderliche Zeit sehr eng mit dem Ergebnis von Fib (N) zusammenhängt, wenn Sie davon ausgehen, dass eine Addition eine Recheneinheit benötigt.
Infolgedessen können Sie direkt zur sehr engen Annäherung der Fibonacci-Reihe springen:
und sagen Sie daher, dass die schlechteste Leistung des naiven Algorithmus ist
PS: Es gibt eine Diskussion über den Ausdruck der N-ten Fibonacci-Zahl in geschlossener Form bei Wikipedia, wenn Sie weitere Informationen wünschen.
quelle
Sie können es erweitern und eine Visulisierung haben
quelle
<
am Ende einen weniger als Charakter ? Wie bist du gekommenT(n-1) + T(n-1)
?T(n-1) > T(n-2)
na ja ... Also kann ich mich ändernT(n-2)
und setzenT(n-1)
. Ich werde nur eine höhere Grenze bekommen, die noch gültig ist fürT(n-1) + T(n-2)
Es wird am unteren Ende durch
2^(n/2)
und am oberen Ende durch 2 ^ n begrenzt (wie in anderen Kommentaren angegeben). Und eine interessante Tatsache dieser rekursiven Implementierung ist, dass sie eine enge asymptotische Grenze von Fib (n) selbst aufweist. Diese Fakten können zusammengefasst werden:Die enge Bindung kann mit ihrer geschlossenen Form weiter reduziert werden , wenn Sie möchten.
quelle
Die Beweisantworten sind gut, aber ich muss immer ein paar Iterationen von Hand machen, um mich wirklich zu überzeugen. Also zog ich einen kleinen Aufrufbaum auf mein Whiteboard und begann, die Knoten zu zählen. Ich teile meine Zählungen in Gesamtknoten, Blattknoten und innere Knoten auf. Folgendes habe ich bekommen:
Was sofort herausspringt, ist, dass die Anzahl der Blattknoten ist
fib(n)
. Was einige weitere Iterationen dauerte, um festzustellen, ist die Anzahl der inneren Knotenfib(n) - 1
. Daher ist die Gesamtzahl der Knoten2 * fib(n) - 1
.Da Sie die Koeffizienten bei der Klassifizierung der Rechenkomplexität fallen lassen, lautet die endgültige Antwort
θ(fib(n))
.quelle
1
zu einer einzelnen AkkumulatorzeitFib(n)
, sondern auch interessant, dass es immer noch genau istθ(Fib(n))
.0
: Rekursionsbasisfälle sind0
und1
, weil sie es tunFib(n-1) + Fib(n-2)
. Daher ist die Antwort3 * Fib(n) - 2
aus dieser Nur-Link-Antwort wahrscheinlich genauer für die Gesamtzahl der Knoten, nicht2 * Fib(n) - 1
.Die zeitliche Komplexität des rekursiven Algorithmus kann besser geschätzt werden, indem ein Rekursionsbaum gezeichnet wird. In diesem Fall wäre die Wiederholungsrelation für das Zeichnen eines Rekursionsbaums T (n) = T (n-1) + T (n-2) + O (1) Jeder Schritt benötigt O (1), was eine konstante Zeit bedeutet, da nur ein Vergleich durchgeführt wird, um den Wert von n in if block zu überprüfen. Der Rekursionsbaum würde so aussehen
Nehmen wir an, jede Ebene des obigen Baums wird daher mit i bezeichnet.
Nehmen wir an, bei einem bestimmten Wert von i endet der Baum. Dieser Fall wäre, wenn ni = 1 ist, also i = n-1, was bedeutet, dass die Höhe des Baums n-1 ist. Lassen Sie uns nun sehen, wie viel Arbeit für jede der n Ebenen im Baum erledigt wird. Beachten Sie, dass jeder Schritt O (1) Zeit benötigt, wie in der Wiederholungsrelation angegeben.
da i = n-1 die Höhe des Baumes ist, wird die auf jeder Ebene geleistete Arbeit ausgeführt
Die Gesamtarbeit ergibt also die Summe der auf jeder Ebene geleisteten Arbeit, daher 2 ^ 0 + 2 ^ 1 + 2 ^ 2 + 2 ^ 3 ... + 2 ^ (n-1), da i = n-1. Durch geometrische Reihen ist diese Summe 2 ^ n, daher ist die Gesamtzeitkomplexität hier O (2 ^ n)
quelle
Nun, meiner Meinung nach ist es so,
O(2^n)
als ob in dieser Funktion nur die Rekursion die beträchtliche Zeit in Anspruch nimmt (Teilen und Erobern). Wir sehen, dass die obige Funktion in einem Baum fortgesetzt wird, bis sich die Blätter nähern, wenn wir das Niveau erreichen,F(n-(n-1))
dhF(1)
. Wenn wir also die Zeitkomplexität aufschreiben, die in jeder Baumtiefe auftritt, lautet die Summationsreihe:das ist die Reihenfolge von
2^n [ O(2^n) ]
.quelle
Die naive Rekursionsversion von Fibonacci ist aufgrund von Wiederholungen in der Berechnung konstruktionsbedingt exponentiell:
An der Wurzel berechnen Sie:
F (n) hängt von F (n-1) und F (n-2) ab
F (n-1) hängt wieder von F (n-2) ab und F (n-3)
F (n-2) hängt wieder von F (n-3) ab und F (n-4)
Wenn Sie dann auf jeder Ebene 2 rekursive Aufrufe haben, die viele Daten in der Berechnung verschwenden, sieht die Zeitfunktion folgendermaßen aus:
T (n) = T (n-1) + T (n-2) + C mit konstanter C.
T (n-1) = T (n-2) + T (n-3)> T (n-2) dann
T (n)> 2 · T (n - 2)
...
T (n)> 2 ^ (n / 2) * T (1) = O (2 ^ (n / 2))
Dies ist nur eine Untergrenze, die für den Zweck Ihrer Analyse ausreichen sollte, aber die Echtzeitfunktion ist ein Faktor einer Konstanten nach derselben Fibonacci-Formel, und die geschlossene Form ist bekanntermaßen exponentiell für den Goldenen Schnitt.
Darüber hinaus können Sie optimierte Versionen von Fibonacci mit dynamischer Programmierung wie folgt finden:
Das ist optimiert und macht nur n Schritte, ist aber auch exponentiell.
Kostenfunktionen werden von der Eingabegröße bis zur Anzahl der Schritte zur Lösung des Problems definiert. Wenn Sie die dynamische Version von Fibonacci sehen ( n Schritte zum Berechnen der Tabelle) oder den einfachsten Algorithmus, um festzustellen, ob eine Zahl eine Primzahl ist ( sqrt (n) , um die gültigen Teiler der Zahl zu analysieren). Sie können denken, dass diese Algorithmen O (n) oder O (sqrt (n)) sind, aber dies ist aus folgendem Grund einfach nicht wahr: Die Eingabe in Ihren Algorithmus ist eine Zahl: n , wobei die binäre Notation die Eingabegröße für a verwendet Ganzzahl n ist log2 (n) und führt dann eine Variablenänderung von durch
Lassen Sie uns die Anzahl der Schritte als Funktion der Eingabegröße herausfinden
dann sind die Kosten Ihres Algorithmus in Abhängigkeit von der Eingabegröße:
und deshalb sind die Kosten exponentiell.
quelle
Es ist einfach zu berechnen, indem Funktionsaufrufe grafisch dargestellt werden. Fügen Sie einfach die Funktionsaufrufe für jeden Wert von n hinzu und sehen Sie, wie die Zahl wächst.
Das große O ist O (Z ^ n), wobei Z der goldene Schnitt oder etwa 1,62 ist.
Sowohl die Leonardo-Zahlen als auch die Fibonacci-Zahlen nähern sich diesem Verhältnis, wenn wir n erhöhen.
Im Gegensatz zu anderen Big O-Fragen gibt es keine Variabilität in der Eingabe und sowohl der Algorithmus als auch die Implementierung des Algorithmus sind klar definiert.
Es ist keine Menge komplexer Mathematik erforderlich. Stellen Sie einfach die folgenden Funktionsaufrufe dar und passen Sie eine Funktion an die Zahlen an.
Oder wenn Sie mit dem Goldenen Schnitt vertraut sind, werden Sie ihn als solchen erkennen.
Diese Antwort ist korrekter als die akzeptierte Antwort, die behauptet, dass sie sich f (n) = 2 ^ n nähert. Das wird es nie. Es wird sich f (n) = golden_ratio ^ n nähern.
quelle
http://www.ics.uci.edu/~eppstein/161/960109.html
Zeit (n) = 3F (n) - 2
quelle