Je comprends la notation Big-O, mais je ne sais pas comment la calculer pour de nombreuses fonctions. En particulier, j'ai essayé de comprendre la complexité de calcul de la version naïve de la séquence de Fibonacci:
int Fibonacci(int n)
{
if (n <= 1)
return n;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Quelle est la complexité de calcul de la séquence de Fibonacci et comment est-elle calculée?
Réponses:
Vous modélisez la fonction de temps à calculer
Fib(n)
comme la somme du temps à calculerFib(n-1)
plus le temps à calculerFib(n-2)
plus le temps de les additionner (O(1)
). Cela suppose que des évaluations répétées du mêmeFib(n)
prennent le même temps - c'est-à-dire qu'aucune mémorisation n'est utilisée.T(n<=1) = O(1)
T(n) = T(n-1) + T(n-2) + O(1)
Vous résolvez cette relation de récurrence (en utilisant des fonctions de génération, par exemple) et vous vous retrouverez avec la réponse.
Alternativement, vous pouvez dessiner l'arbre de récursivité, qui aura de la profondeur
n
et comprendre intuitivement que cette fonction est asymptotique . Vous pouvez ensuite prouver votre conjecture par induction.O(2
n
)
Base:
n = 1
est évidentSupposons , par conséquent
T(n-1) = O(2
n-1
)
T(n) = T(n-1) + T(n-2) + O(1)
ce qui est égal àT(n) = O(2
n-1
) + O(2
n-2
) + O(1) = O(2
n
)
Cependant, comme indiqué dans un commentaire, ce n'est pas la limite stricte. Un fait intéressant à propos de cette fonction est que le T (n) est asymptotiquement identique à la valeur de
Fib(n)
puisque les deux sont définis commef(n) = f(n-1) + f(n-2)
.Les feuilles de l'arbre de récursivité renverront toujours 1. La valeur de
Fib(n)
est la somme de toutes les valeurs renvoyées par les feuilles de l'arbre de récursivité qui est égale au nombre de feuilles. Puisque chaque feuille prendra O (1) pour calculer,T(n)
est égal àFib(n) x O(1)
. Par conséquent, la limite stricte de cette fonction est la séquence de Fibonacci elle-même (~ ). Vous pouvez découvrir cette limite stricte en utilisant des fonctions de génération comme je l'ai mentionné ci-dessus.θ(1.6
n
)
la source
Demandez-vous simplement combien d'instructions doivent
F(n)
être exécutées pour terminer.Pour
F(1)
, la réponse est1
(la première partie du conditionnel).Car
F(n)
, la réponse estF(n-1) + F(n-2)
.Alors quelle fonction satisfait ces règles? Essayez un n (a> 1):
a n == a (n-1) + a (n-2)
Divisez par un (n-2) :
a 2 == a + 1
Résolvez
a
et vous obtenez(1+sqrt(5))/2 = 1.6180339887
, autrement connu comme le nombre d' or .Cela prend donc un temps exponentiel.
la source
Je suis d'accord avec pgaur et rickerbh, la complexité de fibonacci-récursif est O (2 ^ n).
Je suis arrivé à la même conclusion par un raisonnement plutôt simpliste mais je crois toujours valable.
Tout d'abord, il s'agit de déterminer combien de fois la fonction fibonacci récursive (F () à partir de maintenant) est appelée lors du calcul du nombre Nth fibonacci. S'il est appelé une fois par nombre dans la séquence 0 à n, alors nous avons O (n), s'il est appelé n fois pour chaque nombre, alors nous obtenons O (n * n), ou O (n ^ 2), etc.
Ainsi, lorsque F () est appelé pour un nombre n, le nombre de fois que F () est appelé pour un nombre donné entre 0 et n-1 augmente à mesure que nous approchons de 0.
Comme première impression, il me semble que si nous le mettons de manière visuelle, en dessinant une unité par temps F () est appelé pour un nombre donné, humide obtenir une sorte de forme pyramidale (c'est-à-dire, si nous centrons les unités horizontalement ). Quelque chose comme ça:
Maintenant, la question est, à quelle vitesse la base de cette pyramide s'élargit-elle à mesure que n grandit?
Prenons un cas réel, par exemple F (6)
Nous voyons que F (0) est appelé 32 fois, ce qui correspond à 2 ^ 5, ce qui correspond à 2 ^ (n-1) pour cet exemple.
Maintenant, nous voulons savoir combien de fois F (x) est appelé, et nous pouvons voir que le nombre de fois que F (0) est appelé n'est qu'une partie de cela.
Si nous déplaçons mentalement tous les * des lignes F (6) à F (2) dans la ligne F (1), nous voyons que les lignes F (1) et F (0) sont maintenant de longueur égale. Ce qui signifie que le nombre total de fois où F () est appelé lorsque n = 6 est 2x32 = 64 = 2 ^ 6.
Maintenant, en termes de complexité:
la source
Il y a une très belle discussion sur ce problème spécifique au MIT . À la page 5, ils indiquent que, si vous supposez qu'un ajout prend une unité de calcul, le temps requis pour calculer Fib (N) est très étroitement lié au résultat de Fib (N).
En conséquence, vous pouvez passer directement à l'approximation très proche de la série Fibonacci:
et dire, par conséquent, que la pire performance de l'algorithme naïf est
PS: Il y a une discussion sur l' expression sous forme fermée du nombre Nth Fibonacci sur Wikipedia si vous souhaitez plus d'informations.
la source
Vous pouvez l'étendre et avoir une visulisation
la source
<
à la fin? Comment es-tu arrivéT(n-1) + T(n-1)
?T(n-1) > T(n-2)
Je peux donc changerT(n-2)
et mettreT(n-1)
. Je n'obtiendrai qu'une limite supérieure qui est toujours valable pourT(n-1) + T(n-2)
Il est délimité à l'extrémité inférieure par
2^(n/2)
et à l'extrémité supérieure par 2 ^ n (comme indiqué dans d'autres commentaires). Et un fait intéressant de cette implémentation récursive est qu'elle a une limite asymptotique étroite de Fib (n) elle-même. Ces faits peuvent être résumés:Le lien serré peut être encore réduit en utilisant sa forme fermée si vous le souhaitez.
la source
Les preuves sont bonnes, mais je dois toujours faire quelques itérations à la main pour vraiment me convaincre. J'ai donc dessiné un petit arbre d'appel sur mon tableau blanc et commencé à compter les nœuds. J'ai divisé mes comptes en nœuds totaux, nœuds feuilles et nœuds intérieurs. Voici ce que j'ai obtenu:
Ce qui saute immédiatement aux yeux, c'est que le nombre de nœuds foliaires est
fib(n)
. Ce qui a pris quelques itérations de plus à remarquer, c'est que le nombre de nœuds intérieurs estfib(n) - 1
. Par conséquent, le nombre total de nœuds est2 * fib(n) - 1
.Puisque vous supprimez les coefficients lors de la classification de la complexité de calcul, la réponse finale est
θ(fib(n))
.la source
1
à un seulFib(n)
temps d' accumulateur , mais il est intéressant de noter que c'est toujours exactementθ(Fib(n))
.0
, cependant: les cas de base de récursion sont0
et1
, car ils le fontFib(n-1) + Fib(n-2)
. Donc, probablement à3 * Fib(n) - 2
partir de cette réponse de lien uniquement est plus précis pour le nombre total de nœuds, non2 * Fib(n) - 1
.La complexité temporelle de l'algorithme récursif peut être mieux estimée en dessinant un arbre de récursivité. Dans ce cas, la relation de récurrence pour dessiner un arbre de récursivité serait T (n) = T (n-1) + T (n-2) + O (1) notez que chaque étape prend O (1), ce qui signifie un temps constant, car il ne fait qu'une seule comparaison pour vérifier la valeur de n dans le bloc if .
Ici, disons que chaque niveau de l'arbre ci-dessus est noté i donc,
disons à une valeur particulière de i, l'arbre se termine, ce cas serait lorsque ni = 1, donc i = n-1, ce qui signifie que la hauteur de l'arbre est n-1. Voyons maintenant combien de travail est effectué pour chacune des n couches dans l'arbre.Notez que chaque étape prend O (1) comme indiqué dans la relation de récurrence.
puisque i = n-1 est la hauteur de l'arbre, le travail effectué à chaque niveau sera
Par conséquent, le travail total effectué sera la somme du travail effectué à chaque niveau, il sera donc de 2 ^ 0 + 2 ^ 1 + 2 ^ 2 + 2 ^ 3 ... + 2 ^ (n-1) puisque i = n-1. Par série géométrique, cette somme est 2 ^ n, donc la complexité temporelle totale est ici O (2 ^ n)
la source
Eh bien, selon moi, c'est
O(2^n)
que dans cette fonction, seule la récursivité prend beaucoup de temps (diviser pour mieux régner). Nous voyons que la fonction ci-dessus continuera dans un arbre jusqu'à ce que les feuilles approchent lorsque nous atteignons le niveauF(n-(n-1))
ieF(1)
. Donc, ici, quand nous notons la complexité temporelle rencontrée à chaque profondeur d'arbre, la série de sommations est:c'est l'ordre du
2^n [ O(2^n) ]
.la source
La version récursive naïve de Fibonacci est exponentielle par conception en raison de la répétition dans le calcul:
À la racine, vous calculez:
F (n) dépend de F (n-1) et F (n-2)
F (n-1) dépend à nouveau de F (n-2) et de F (n-3)
F (n-2) dépend à nouveau de F (n-3) et de F (n-4)
alors vous avez à chaque niveau 2 des appels récursifs qui gaspillent beaucoup de données dans le calcul, la fonction de temps ressemblera à ceci:
T (n) = T (n-1) + T (n-2) + C, avec constante C
T (n-1) = T (n-2) + T (n-3)> T (n-2) puis
T (n)> 2 * T (n-2)
...
T (n)> 2 ^ (n / 2) * T (1) = O (2 ^ (n / 2))
C'est juste une limite inférieure qui, aux fins de votre analyse, devrait être suffisante, mais la fonction en temps réel est un facteur d'une constante par la même formule de Fibonacci et la forme fermée est connue pour être exponentielle du nombre d'or.
De plus, vous pouvez trouver des versions optimisées de Fibonacci en utilisant une programmation dynamique comme celle-ci:
Ceci est optimisé et ne fait que n étapes mais est également exponentiel.
Les fonctions de coût sont définies de la taille d'entrée au nombre d'étapes pour résoudre le problème. Lorsque vous voyez la version dynamique de Fibonacci ( n étapes pour calculer la table) ou l'algorithme le plus simple pour savoir si un nombre est premier ( sqrt (n) pour analyser les diviseurs valides du nombre). vous pouvez penser que ces algorithmes sont O (n) ou O (sqrt (n)) mais ce n'est tout simplement pas vrai pour la raison suivante: L'entrée de votre algorithme est un nombre: n , en utilisant la notation binaire la taille d'entrée pour un entier n est log2 (n) faisant alors un changement variable de
permet de connaître le nombre d'étapes en fonction de la taille d'entrée
alors le coût de votre algorithme en fonction de la taille d'entrée est:
et c'est pourquoi le coût est exponentiel.
la source
Il est simple à calculer en schématisant les appels de fonction. Ajoutez simplement les appels de fonction pour chaque valeur de n et regardez comment le nombre augmente.
Le Big O est O (Z ^ n) où Z est le nombre d'or ou environ 1,62.
Les nombres de Leonardo et les nombres de Fibonacci approchent ce rapport lorsque nous augmentons n.
Contrairement à d'autres questions Big O, il n'y a pas de variabilité dans l'entrée et l'algorithme et la mise en œuvre de l'algorithme sont clairement définis.
Il n'y a pas besoin d'un tas de mathématiques complexes. Diagrammez simplement les appels de fonction ci-dessous et ajustez une fonction aux nombres.
Ou si vous connaissez le nombre d'or, vous le reconnaîtrez comme tel.
Cette réponse est plus correcte que la réponse acceptée qui prétend qu'elle approchera f (n) = 2 ^ n. Ça ne le sera jamais. Il approchera f (n) = golden_ratio ^ n.
la source
http://www.ics.uci.edu/~eppstein/161/960109.html
temps (n) = 3F (n) - 2
la source