Efficace de calcul de la suite de Fibonacci
Je suis en train de travailler sur un Projet Euler problème: celui de la somme des nombres de Fibonacci.
Mon code:
def Fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return Fibonacci(n-1) + Fibonacci(n-2)
list1 = [x for x in range(39)]
list2 = [i for i in list1 if Fibonacci(i) % 2 == 0]
La solution du problème peut être facilement trouvé par l'impression de la somme(liste 2). Cependant, c'est prendre beaucoup de temps pour arriver à la liste 2, je suis dans le doute. Est-il possible de faire plus vite? Ou est-il correct, même de cette façon...
(le problème: En considérant les termes de la suite de Fibonacci dont les valeurs ne dépassent pas quatre millions de dollars, la somme de la valeur des termes.)
- P. S. j'ai trouvé les valeurs pour lesquelles elle ne dépasse pas 4 millions de dollars en essayant.
- Astuce: essayez de lire la page wiki...
- Non: la page wiki pour les nombres de Fibonacci....
- Naïf récursivité ne fonctionne que dans O(phi^n)
- On peut calculer dans
O(log n)
. Voir n-ième nombre de fibonacci dans sublinéaire temps. - Projet Euler 's Même nombres de Fibonacci est sur
even-valued terms
, et non pas les valeurs avec la même ordinal/pour les même arguments/au même indice. Si vous pouvez trouver l'ordinal pour le plus grand terme plus petite que la limite (four million
avec "Problème 2"), vous pouvez trouver cette somme en une seule évaluation de la fonction de Fibonacci.
Vous devez vous connecter pour publier un commentaire.
Oui. La primitive récursive solution prend beaucoup de temps. La raison pour cela est que, pour chaque nombre calculé, il a besoin de calculer tous les nombres précédents plus d'une fois. Jetez un oeil à l'image ci-dessous.
Il représente le calcul de
Fibonacci(5)
avec votre fonction. Comme vous pouvez le voir, il calcule la valeur deFibonacci(2)
trois fois, et la valeur deFibonacci(1)
cinq fois. Que devient de pire en pire plus le numéro que vous voulez calculer.Ce qui le rend même pire, c'est qu'avec tous les nombres de fibonacci vous calculer dans votre liste, vous n'avez pas utiliser les numéros précédents d'avoir les connaissances nécessaires pour accélérer la vitesse de calcul permet de calculer chaque nombre "à partir de zéro."
Il ya quelques options pour rendre cela plus rapidement:
1. Créer une liste "par le bas"
La façon la plus simple est de créer une liste de nombres de fibonacci jusqu'à le nombre que vous voulez. Si vous le faites, vous construire "par le bas", ou pour parler, et vous pouvez réutiliser les numéros précédents pour créer le prochain. Si vous avez une liste des nombres de fibonacci
[0, 1, 1, 2, 3]
, vous pouvez utiliser les deux derniers numéros de cette liste pour créer le prochain numéro.Cette approche devrait ressembler à quelque chose comme ceci:
Ensuite, vous pouvez obtenir les 20 premiers nombres de fibonacci en faisant
Ou vous pouvez obtenir le 17e nombre de fibonacci à partir d'une liste de la première 40 en faisant
2. Memoization (relativement avancée technique)
Une autre alternative pour le rendre plus rapide existe, mais il est un peu plus compliqué que de bien. Depuis votre problème est que vous re-calculer les valeurs que vous avez déjà calculé, vous pouvez à la place choisir de sauvegarder les valeurs que vous avez déjà calculée dans un dict, et essayer d'obtenir à partir de l'avant de recalculer eux. Ceci est appelé memoization. Il peut ressembler à quelque chose comme ceci:
Cela vous permet de calculer les grands nombres de fibonacci en un clin d'oeil:
C'est en fait, une telle technique courante que Python 3 comprend un décorateur pour le faire pour vous. Je vous présente, automatique memoization!
Cela fait à peu près la même chose que la fonction précédente, mais avec tous les
computed
trucs traitées par lelru_cache
décorateur.3. Juste compter jusqu' (un naïf solution itérative)
Une troisième méthode, comme suggéré par Mitch, est à juste compter jusqu'sans enregistrer l'intermédiaire de valeurs dans une liste. Vous pourriez imaginer faire
Je ne recommande pas ces deux dernières méthodes, si votre objectif est de créer un liste de nombres de fibonacci.
fib_to(100)
va être beaucoup plus vite que[fib(n) for n in range(101)]
parce qu'avec ce dernier, vous obtenez toujours le problème de calcul de chaque nombre de la liste à partir de zéro.list(fib(N))
. Probablement à un petit gain de performance si. Je n'ai pas lu toute la réponse. Je suis la gueule de bois.n
th même nombres de Fibonacci à l'aide d'un générateur est absent de cette réponse complète cependant. Vous pouvez trouver une mise en œuvre dans ma réponse ci-dessous. @kqr: Aucune idée pourquoi LRU cache décorateur est beaucoup plus lent que d'une main memoized solution?in fib computed[n] = fib(n-1, computed) + fib(n-2, computed) [Previous line repeated 995 more times] RecursionError: maximum recursion depth exceeded
C'est un très algorithme rapide et il peut trouver le n-ième nombre de Fibonacci beaucoup plus rapide que la simple approche itérative présentés dans les autres réponses, il est très avancé si:
Vous pouvez lire un peu plus à propos impliqués mathématiques ici.
n
que pythonfloat
s sont d'une précision limitée, à la différence de laint
sn % 2
,n //= 2
pourrec
au lieu de l'opération de chaînebin
.Python n'est pas d'optimiser la queue de la récursivité, donc la plupart des solutions présentées ici échouera avec
Error: maximum recursion depth exceeded in comparison
sin
est trop gros (et en gros, je veux dire 1000).La limite de la récursivité peut être augmenté, mais il fera de Python crash sur un débordement de pile dans le système d'exploitation.
Remarque la différence de performance entre
fib_memo
/fib_local
etfib_lru
/fib_local_exc
: LRU cache est beaucoup plus lent et n'a même pas complet, car il génère une erreur d'exécution déjà pour n = ~500:Résultats:
La solution itérative est de loin la manière la plus rapide et ne pas endommager la pile, même pour
n=100k
(0.162 secondes). Il ne renvoie pas l'intermédiaire de la suite de Fibonacci en effet.Si vous voulez calculer le
n
th même nombre de Fibonacci, vous pourriez adapter l'approche itérative comme ceci:Ou si vous êtes intéressé, à chaque numéro sur le chemin, utilisez un générateur:
Résultat:
C'est le premier de 100, même nombres de Fibonacci dans ~7ms et inclut les frais généraux de l'impression à la borne (facile de sous-estimer sur Windows).
a, b = 0, 2
eta, b = b, a + 4*b
.)(n - 1).times.reduce([0, 1]) { |array| [array[1], array[0] + array[1]] }.last
Basée sur le fait que
fib(n) = fib(n-1)+fib(n-2)
, la solution simple estcependant, le problème ici est que certaines valeurs sont calculées à plusieurs reprises, et par conséquent, il est très inefficace. La raison peut être vu dans ce sketch:
Essentiellement, chaque appel récursif de
fib
fonction doit calculer toutes les précédentes nombres de fibonacci pour son propre usage. Ainsi, la plupart des valeur calculée sera fib(1), car elle doit apparaître dans tous les nœuds feuilles de l'arbre illustré par la réponse de @kqr. La complexité de cet algorithme est le nombre de nœuds de l'arbre, qui est de $O(2^n)$.Maintenant une meilleure façon est de garder la trace de deux nombres, la valeur actuelle et la valeur précédente, de sorte que chaque appel n'a pas à calculer toutes les valeurs précédentes. C'est la deuxième algorithme dans l'esquisse, et peut être mis en œuvre comme suit
La complexité de cet algorithme est linéaire en $O(n)$, et certains exemples seront
Solution dans R, l'indice de référence calcule 1 à 1000e nombre de Fibonacci de la série dans 1,9 secondes. Serait beaucoup plus rapide en C++ ou Fortran, en fait, depuis la rédaction de mon post initial, j'ai écrit une fonction équivalente en C++ et qui a terminé dans un impressionnant 0.0033 quelques secondes, même en python complété en 0,3 secondes.
J'ai basé cette sur un article sur les nombres de Fibonacci sur Wikipédia. L'idée est d'éviter le bouclage et la récursivité et il suffit de calculer la valeur en tant que de besoin.
De ne pas être un math wiz, choisi parmi les formules et l'a rendu à code et modifié jusqu'à ce que les valeurs est sorti.
[finding] the sum of the even-valued terms not [exceeding] four million
rapide?kqr's solution n ° 2 est mon définitive favori.
Toutefois, dans ce cas précis nous perdre tous nos calculs entre conséquente des appels dans la compréhension de liste:
j'ai donc décidé d'aller plus loin et memoize il entre en boucle étapes, comme suit:
Il y a un O(1) solution: https://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding
Des problèmes comme cela va prendre un certain temps si il y a beaucoup de niveaux de récursivité. La définition récursive est bon pour le codage du problème d'une manière qui peut être facilement compris, mais si vous en avez besoin pour courir plus vite une solution itérative comme la réponse dans ce fil sera beaucoup plus rapide.
Récursive de calcul de Fibonacci sera plus inefficace que de le faire de manière itérative. Ma recommandation est:
Prendre le temps de créer un
Fibonacci
classe comme un itérateur, et de faire les calculs de façon indépendante pour chaque élément de l'index, peut-être avec quelques@memoize
décorateur (et aussi ici) pour mettre en cache tous les calculs précédents.Espérons que cette aide!
Un moyen rapide de calculer l'fib(n/2) nombre de façon récursive:
Haskell 1 liner :-
Ce code est extrêmement efficace et calcule les nombres de Fibonacci jusqu'à (
10^1000
) en moins d'une seconde !Ce code sera également utile pour ce problème dans le Projet Euler.
Pour trouver la somme de la première
n
même valeur nombres de fibonacci directement, mettre3n + 2
dans vos favoris méthode efficace de calculer un unique nombre de fibonacci, décrémenter par un et de les diviser par deux (fib((3*n+2) - 1)/2)
). Comment avez-maths nuls survivre avant de OEIS?Vous pouvez utiliser l'équation avec des racines carrées pour le calcul de ce si vous n'utilisez pas l'arithmétique à virgule flottante, mais garder la trace des coefficients d'une autre façon que vous allez. Cela donne essentiellement une constante de temps exacte de l'algorithme pour des nombres de Fibonacci:
C'est une version améliorée de Fibonacci où nous calculer Fibonacci de nombre qu'une seule fois:
Ici, nous sommes de stockage de Fibonacci de chaque nombre dans le dictionnaire. De sorte que vous pouvez le voir, il calcule qu'une seule fois pour chaque itération et pour Fibonacci(10) est à seulement 9 fois.
Un O(1) solution
Il s'avère qu'il y a une belle formule récursive pour la somme de même nombres de Fibonacci. Le n-ième terme de la séquence de sommes de même nombres de Fibonacci est
S_{n} = 4*S_{n-1} + S_{n-2} + 2
la Preuve est laissée au lecteur, mais consiste à prouver (1) Fibo sont les numéros de chaque troisième, 2) la preuve de la formule ci-dessus avec l'induction, en utilisant la définition de Fibo numéros. À l'aide de la logique de ici, nous pouvons dériver une forme fermée formule pour cela avec un peu d'effort:S_{n} = -1/2 + (1/4 + 3*sqrt(5)/20)*(2+sqrt(5))**n + (1/4 - 3*sqrt(5)/20)*(2-sqrt(5))**n
Malgré la
sqrt
, cela fait partie intégrante de l'intégralen
, donc cela peut être facilement calculée en utilisant la pratique des fonctions à partir de ma réponse précédente, ou à l'aide d'un package commesympy
pour gérer les racines exactement.O(1) SOLUTION
La formule est également appelé Binet Formule (lire plus)
Fondamentalement, nous pouvons l'écrire dans
python
comme ceci:Toutefois, en Raison de la relativement faible de la valeur de b, on peut l'ignorer et de la fonction peut être aussi simple que de
Bien qu'une réponse tardive mais il pourrait être utile
C'est beaucoup plus rapide que la méthode traditionnelle
Donné le numéro de départ et le nombre maximum; je pense que la solution suivante pour fibonacci serait intéressant. La bonne chose est qu'il ne comprend pas la récursivité, réduire le fardeau de la mémoire.
Voici un exemple simple sans récursivité et de O(n)
Spoiler alerte: ne lisez pas ceci si vous êtes en train de faire du Projet Euler Question 2 jusqu'à ce que vous avez eu une fissure à elle-même.
De forme fermée de la série-somme-approches basées sur le côté, ce qui semble plus efficace que la plupart de ce que j'ai vu posté, car il ne nécessite qu'un plutôt bon marché itération de boucle par même nombre de Fibonacci, de sorte que seulement 12 itérations pour atteindre 4 000 000 d'.
Ici est une Solution Optimisée avec le Dictionnaire
pour
n=30
:pour
n=300
:pour
n=3000
:pour
n=30000
:pour
n=300000
:pour
n=3000000
:pour
n=30000000
:avertissement: codes de fonctions. 4 et 5 n'ont pas été écrit par moi