Je travaille sur un problème Project Euler : celui qui concerne la somme des nombres pairs 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ée en imprimant sum (list2). Cependant, il me faut beaucoup de temps pour dresser la liste2. Est-il possible d'accélérer le processus? Ou est-ce que ça va 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, trouvez la somme des termes de valeur paire.)
Oui. La solution récursive primitive prend beaucoup de temps. La raison en est que pour chaque nombre calculé, il doit calculer tous les nombres précédents plusieurs fois. Regardez l'image suivante.
Cela représente le calcul de Fibonacci(5)
avec votre fonction. Comme vous pouvez le constater, il calcule la valeur de Fibonacci(2)
trois fois et la valeur de Fibonacci(1)
cinq fois. Cela devient de pire en pire avec le nombre que vous souhaitez calculer.
Ce qui le rend pire pair _ c'est qu'avec chaque nombre de fibonacci calculé dans votre liste, vous n'utilisez pas les chiffres précédents dont vous avez connaissance pour accélérer le calcul - vous calculez chaque nombre "à partir de zéro".
Il y a quelques options pour rendre cela plus rapide:
Le moyen le plus simple est de simplement créer une liste de nombres de fibonacci jusqu’au nombre que vous voulez. Si vous faites cela, vous construisez "de bas en haut" ou pour ainsi dire, et vous pouvez réutiliser les numéros précédents pour créer le prochain. Si vous avez une liste des numéros de fibonacci [0, 1, 1, 2, 3]
, vous pouvez utiliser les deux derniers chiffres de cette liste pour créer le numéro suivant.
Cette approche ressemblerait à ceci:
>>> def fib_to(n):
... fibs = [0, 1]
... for i in range(2, n+1):
... fibs.append(fibs[-1] + fibs[-2])
... return fibs
...
Ensuite, vous pouvez obtenir les 20 premiers nombres de fibonacci en faisant
>>> fib_to(20)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
Ou vous pouvez obtenir le 17ème numéro de fibonacci à partir d'une liste des 40 premiers en faisant
>>> fib_to(40)[17]
1597
Une autre alternative pour accélérer le processus existe, mais elle est également un peu plus compliquée. Puisque votre problème est que vous recalculez les valeurs que vous avez déjà calculées, vous pouvez plutôt choisir de sauvegarder les valeurs que vous avez déjà calculées dans un dict, et essayer de les récupérer avant de les recalculer. Ceci s'appelle memoization. Cela peut ressembler à quelque chose comme ça:
>>> def fib(n, computed = {0: 0, 1: 1}):
... if n not in computed:
... computed[n] = fib(n-1, computed) + fib(n-2, computed)
... return computed[n]
Cela vous permet de calculer de grands nombres de fibonacci en un tournemain:
>>> fib(400)
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
C'est en fait une technique si courante que Python 3 inclut un décorateur pour le faire pour vous. Je vous présente la mémoisation automatique!
import functools
@functools.lru_cache(None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
Cela fait à peu près la même chose que la fonction précédente, mais avec tous les éléments computed
gérés par le décorateur lru_cache
.
Une troisième méthode, suggérée par Mitch, consiste simplement à compter sans enregistrer les valeurs intermédiaires dans une liste. Vous pourriez imaginer faire
>>> def fib(n):
... a, b = 0, 1
... for _ in range(n):
... a, b = b, a+b
... return a
Je ne recommande pas ces deux dernières méthodes si votre objectif est de créer une liste de nombres fibonacci. fib_to(100)
va être beaucoup plus rapide que [fib(n) for n in range(101)]
car avec ce dernier problème, le problème de calcul de chaque numéro de la liste reste entier.
Il s’agit d’un algorithme très rapide qui permet de trouver le nième nombre de Fibonacci beaucoup plus rapidement que la simple approche itérative présentée dans d’autres réponses. Il est cependant assez avancé:
def fib(n):
v1, v2, v3 = 1, 1, 0 # initialise a matrix [[1,1],[1,0]]
for rec in bin(n)[3:]: # perform fast exponentiation of the matrix (quickly raise it to the nth power)
calc = v2*v2
v1, v2, v3 = v1*v1+calc, (v1+v3)*v2, calc+v3*v3
if rec=='1': v1, v2, v3 = v1+v2, v1, v2
return v2
Vous pouvez en savoir plus sur les mathématiques impliquées ici .
Python n'optimisant pas la récursion finale, la plupart des solutions présentées ici échoueront avec Error: maximum recursion depth exceeded in comparison
si n
est trop volumineux (et gros, je veux dire 1000).
La limite de récursivité peut être augmentée, mais cela provoquera un blocage de Python lors du débordement de la pile dans le système d'exploitation.
Notez la différence de performances entre fib_memo
fib_local
et fib_lru
/fib_local_exc
: le cache LRU est beaucoup plus lent et n'est même pas complet, car il génère déjà une erreur d'exécution pour n = ~ 500:
import functools
from time import clock
#import sys
#sys.setrecursionlimit()
@functools.lru_cache(None)
def fib_lru(n):
if n < 2:
return n
return fib_lru(n-1) + fib_lru(n-2)
def fib_memo(n, computed = {0: 0, 1: 1}):
if n not in computed:
computed[n] = fib_memo(n-1, computed) + fib_memo(n-2, computed)
return computed[n]
def fib_local(n):
computed = {0: 0, 1: 1}
def fib_inner(n):
if n not in computed:
computed[n] = fib_inner(n-1) + fib_inner(n-2)
return computed[n]
return fib_inner(n)
def fib_local_exc(n):
computed = {0: 0, 1: 1}
def fib_inner_x(n):
try:
computed[n]
except KeyError:
computed[n] = fib_inner_x(n-1) + fib_inner_x(n-2)
return computed[n]
return fib_inner_x(n)
def fib_iter(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
def benchmark(n, *args):
print("-" * 80)
for func in args:
print(func.__name__)
start = clock()
try:
ret = func(n)
#print("Result:", ret)
except RuntimeError as e:
print("Error:", e)
print("Time:", "{:.8f}".format(clock() - start))
print()
benchmark(500, fib_iter, fib_memo, fib_local, fib_local_exc, fib_lru)
Résultats:
fib_iter
Time: 0.00008168
fib_memo
Time: 0.00048622
fib_local
Time: 0.00044645
fib_local_exc
Time: 0.00146036
fib_lru
Error: maximum recursion depth exceeded in comparison
Time: 0.00112552
La solution itérative est de loin la plus rapide et ne corrompt pas la pile, même pour n=100k
(0,162 seconde). Il ne renvoie pas les nombres intermédiaires de Fibonacci en effet.
Si vous voulez calculer le nombre n
th même Fibonacci, vous pouvez adapter l'approche itérative comme ceci:
def fib_even_iter(n):
a, b = 0, 1
c = 1
while c < n:
a, b = b, a + b
if a % 2 == 0:
c += 1
return a
Ou si vous êtes intéressé par tous les nombres pairs à venir, utilisez un _/generator:
def fib_even_gen(n):
a, b = 0, 1
c = 1
yield a
while c < n:
a, b = b, a + b
if a % 2 == 0:
yield a
c += 1
return a
for i, f in enumerate(fib_even_gen(100), 1):
print("{:3d}. {:d}".format(i, f))
Résultat:
1. 0
2. 2
3. 8
4. 34
5. 144
6. 610
7. 2584
8. 10946
9. 46368
10. 196418
11. 832040
12. 3524578
13. 14930352
14. 63245986
15. 267914296
16. 1134903170
17. 4807526976
18. 20365011074
19. 86267571272
20. 365435296162
21. 1548008755920
22. 6557470319842
23. 27777890035288
24. 117669030460994
25. 498454011879264
26. 2111485077978050
27. 8944394323791464
28. 37889062373143906
29. 160500643816367088
30. 679891637638612258
31. 2880067194370816120
32. 12200160415121876738
33. 51680708854858323072
34. 218922995834555169026
35. 927372692193078999176
36. 3928413764606871165730
37. 16641027750620563662096
38. 70492524767089125814114
39. 298611126818977066918552
40. 1264937032042997393488322
41. 5358359254990966640871840
42. 22698374052006863956975682
43. 96151855463018422468774568
44. 407305795904080553832073954
45. 1725375039079340637797070384
46. 7308805952221443105020355490
47. 30960598847965113057878492344
48. 131151201344081895336534324866
49. 555565404224292694404015791808
50. 2353412818241252672952597492098
51. 9969216677189303386214405760200
52. 42230279526998466217810220532898
53. 178890334785183168257455287891792
54. 757791618667731139247631372100066
55. 3210056809456107725247980776292056
56. 13598018856492162040239554477268290
57. 57602132235424755886206198685365216
58. 244006547798191185585064349218729154
59. 1033628323428189498226463595560281832
60. 4378519841510949178490918731459856482
61. 18547707689471986212190138521399707760
62. 78569350599398894027251472817058687522
63. 332825110087067562321196029789634457848
64. 1409869790947669143312035591975596518914
65. 5972304273877744135569338397692020533504
66. 25299086886458645685589389182743678652930
67. 107168651819712326877926895128666735145224
68. 453973694165307953197296969697410619233826
69. 1923063428480944139667114773918309212080528
70. 8146227408089084511865756065370647467555938
71. 34507973060837282187130139035400899082304280
72. 146178119651438213260386312206974243796773058
73. 619220451666590135228675387863297874269396512
74. 2623059926317798754175087863660165740874359106
75. 11111460156937785151929026842503960837766832936
76. 47068900554068939361891195233676009091941690850
77. 199387062373213542599493807777207997205533596336
78. 844617150046923109759866426342507997914076076194
79. 3577855662560905981638959513147239988861837901112
80. 15156039800290547036315704478931467953361427680642
81. 64202014863723094126901777428873111802307548623680
82. 271964099255182923543922814194423915162591622175362
83. 1152058411884454788302593034206568772452674037325128
84. 4880197746793002076754294951020699004973287771475874
85. 20672849399056463095319772838289364792345825123228624
86. 87571595343018854458033386304178158174356588264390370
87. 370959230771131880927453318055001997489772178180790104
88. 1571408518427546378167846658524186148133445300987550786
89. 6656593304481317393598839952151746590023553382130993248
90. 28197781736352815952563206467131172508227658829511523778
91. 119447720249892581203851665820676436622934188700177088360
92. 505988662735923140767969869749836918999964413630219877218
93. 2143402371193585144275731144820024112622791843221056597232
94. 9079598147510263717870894449029933369491131786514446266146
95. 38461794961234640015759308940939757590587318989278841661816
96. 162926777992448823780908130212788963731840407743629812913410
97. 690168906931029935139391829792095612517948949963798093315456
98. 2923602405716568564338475449381171413803636207598822186175234
99. 12384578529797304192493293627316781267732493780359086838016392
100. 52461916524905785334311649958648296484733611329035169538240802
Time: 0.00698620
C’est la première centaine de nombres, même Fibonacci, en environ 7 ms et inclut les frais généraux liés à l’impression sur un terminal (facile à sous-estimer sous Windows).
Sur la base du fait que fib(n) = fib(n-1)+fib(n-2)
, la solution simple est:
def fib(n):
if (n <=1):
return(1)
else:
return(fib(n-1)+fib(n-2))
cependant, le problème ici est que certaines valeurs sont calculées plusieurs fois, ce qui la rend très inefficace. La raison peut être vue dans ce croquis:
Essentiellement, chaque appel récursif à la fonction fib
doit calculer tous les numéros de fibonacci précédents pour son propre usage. Ainsi, la valeur la plus calculée sera fib (1) puisqu'elle doit apparaître dans tous les nœuds feuilles de l'arbre indiqué par la réponse de @kqr. La complexité de cet algorithme est le nombre de nœuds de l’arbre, qui est $ O (2 ^ n) $.
Maintenant, un meilleur moyen est de garder trace de deux nombres, la valeur actuelle et la valeur précédente, afin que chaque appel ne doive pas calculer toutes les valeurs précédentes. C'est le deuxième algorithme de l'esquisse, et peut être implémenté comme suit
def fib(n):
if (n==0):
return(0,1)
Elif (n==1):
return(1,1)
else:
a,b = fib(n-1)
return(b,a+b)
La complexité de cet algorithme est linéaire $ O (n) $, et quelques exemples seront
>>> fib(1)
(1, 1)
>>> fib(2)
(1, 2)
>>> fib(4)
(3, 5)
>>> fib(6)
(8, 13)
Solution in R, benchmark calcule de 1 à 1000ème série de nombres de Fibonacci en 1.9 secondes. Ce serait beaucoup plus rapide en C++ ou en Fortran, en fait, depuis l'écriture du post initial, j'ai écrit une fonction équivalente en C++ qui s'est terminée en un impressionnant 0.0033 seconde, voire même en python en 0.3 seconde.
#Calculate Fibonnaci Sequence
fib <- function(n){
if(n <= 2)
return(as.integer(as.logical(n)))
k = as.integer(n/2)
a = fib(k + 1)
b = fib(k)
if(n %% 2 == 1)
return(a*a + b*b)
return(b*(2*a - b))
}
#Function to do every fibonacci number up to nmax
doFib <- function(nmax = 25,doPrint=FALSE){
res = sapply(0:abs(nmax),fib)
if(doPrint)
print(paste(res,collapse=","))
return(res)
}
#Benchmark
system.time(doFib(1000))
#user system elapsed
# 1.874 0.007 1.892
Je me suis basé sur un article sur les chiffres de Fibonacci sur Wikipedia. L'idée est d'éviter les boucles et les récursions et de calculer simplement la valeur en fonction des besoins.
N'étant pas un expert en mathématiques, a sélectionné l'une des formules, l'a convertie en code et l'a modifiée jusqu'à ce que les valeurs apparaissent correctement.
import cmath
def getFib(n):
#Given which fibonacci number we want, calculate its value
lsa = (1 / cmath.sqrt(5)) * pow(((1 + cmath.sqrt(5)) / 2), n)
rsa = (1 / cmath.sqrt(5)) * pow(((1 - cmath.sqrt(5)) / 2), n)
fib = lsa-rsa
#coerce to real so we can round the complex result
fn = round(fib.real)
return fn
#Demo using the function
s = ''
for m in range(0,30):
s = s + '(' + str(m) + ')' + str(getFib(m)) + ' '
print(s)
La solution n ° 2 de kqr est mon préféré.
Cependant, dans ce cas particulier, nous perdons tous nos calculs entre les appels consécutifs au sein de la compréhension de liste:
list2 = [i for i in list1 if fib(i) % 2 == 0]
, j’ai donc décidé d’aller un peu plus loin et de le mémoriser entre les étapes de la boucle comme suit:
def cache_fib(ff):
comp = {0: 0, 1: 1}
def fib_cached(n, computed=comp):
return ff(n, computed)
return fib_cached
@cache_fib
def fib(n, computed={0: 0, 1: 1}):
if n not in computed:
computed[n] = fib(n - 1, computed) + fib(n - 2, computed)
return computed[n]
Le calcul récursif de Fibonacci sera plus inefficace que de le faire par itération. Ma recommandation est la suivante:
Prenez le temps de créer une classe Fibonacci
en tant qu'itérateur et effectuez les calculs indépendamment pour chaque élément de l'index, éventuellement avec un @memoize
décorator (et aussi here ) pour mettre en cache tous les calculs antérieurs.
J'espère que cela t'aides!
Voici une version améliorée de Fibonacci où nous calculons le nombre de Fibonacci une seule fois:
dicFib = { 0:0 ,1 :1 }
iterations = 0
def fibonacci(a):
if (a in dicFib):
return dicFib[a]
else :
global iterations
fib = fibonacci(a-2)+fibonacci(a-1)
dicFib[a] = fib
iterations += 1
return fib
print ("Fibonacci of 10 is:" , fibonacci(10))
print ("Fibonacci of all numbers:" ,dicFib)
print ("iterations:" ,iterations)
# ('Fibonacci of 10 is:', 55)
# ('Fibonacci of all numbers:', {0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55})
# ('iterations:', 9)
Ici, nous stockons Fibonacci de chaque numéro dans le dictionnaire. Vous pouvez donc voir qu’il ne calcule qu’une fois pour chaque itération et pour Fibonacci (10), il n’est que 9 fois.
Pour trouver directement la somme des premiers nombres fibonacci n
à valeurs paires, insérez 3n + 2
dans votre méthode préférée pour calculer efficacement un seul numéro fibonacci, décrémentez-le d'un et divisez-le par deux (fib((3*n+2) - 1)/2)
). Comment les mannequins en maths ont-ils survécu avant OEIS ?
Il existe une solution O(1): https://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding
import math
PHI = (1 + math.sqrt(5)) / 2
SQRT5 = math.sqrt(5)
def fast_fib(n):
if n < 0:
raise ValueError('Fibs for negative values are not defined.')
return round(math.pow(PHI, n) / SQRT5)
Haskell 1 doublure: -
fibs = 0 : (f 1 1) where f a b = a : f b (a+b)
Ce code est extrêmement efficace et calcule les nombres Fibonacci jusqu’à (10^1000
) en moins d’une seconde! Ce code sera également utile pour ce problème dans Project Euler.
Vous pouvez utiliser l'équation avec les racines carrées pour le calculer si vous n'utilisez pas d'arithmétique en virgule flottante, mais suivez les coefficients d'une autre manière. Cela donne un algorithme exact à temps constant pour les nombres de Fibonacci:
def rootiply(a1,b1,a2,b2,c):
''' multipy a1+b1*sqrt(c) and a2+b2*sqrt(c)... return a,b'''
return a1*a2 + b1*b2*c, a1*b2 + a2*b1
def rootipower(a,b,c,n):
''' raise a + b * sqrt(c) to the nth power... returns the new a,b and c of the result in the same format'''
ar,br = 1,0
while n != 0:
if n%2:
ar,br = rootiply(ar,br,a,b,c)
a,b = rootiply(a,b,a,b,c)
n /= 2
return ar,br
def fib(k):
''' the kth fibonacci number'''
a1,b1 = rootipower(1,1,5,k)
a2,b2 = rootipower(1,-1,5,k)
a = a1-a2
b = b1-b2
a,b = rootiply(0,1,a,b,5)
# b should be 0!
assert b == 0
return a/2**k/5
if __== "__main__":
assert rootipower(1,2,3,3) == (37,30) # 1+2sqrt(3) **3 => 13 + 4sqrt(3) => 39 + 30sqrt(3)
assert fib(10)==55
Tous les problèmes de ce type prendront beaucoup de temps s’il ya beaucoup de niveaux de récursivité. La définition récursive est bonne pour coder le problème d'une manière facile à comprendre, mais si vous en avez besoin pour exécuter plus rapidement une solution itérative telle que la réponse dans ce fil sera beaucoup plus rapide.
Une méthode rapide consiste à calculer le nombre fib (n/2) de manière récursive:
fibs = {0: 0, 1: 1}
def fib(n):
if n in fibs: return fibs[n]
if n % 2 == 0:
fibs[n] = ((2 * fib((n / 2) - 1)) + fib(n / 2)) * fib(n / 2)
return fibs[n]
else:
fibs[n] = (fib((n - 1) / 2) ** 2) + (fib((n+1) / 2) ** 2)
return fibs[n]
from time import time
s=time()
print fib(1000000)
print time()-s
O(1) SOLUTION
La formule est aussi appelée Formule de Binet ( en lire plus )
Fondamentalement, nous pouvons l'écrire dans python
comme ceci:
def fib(n):
a = ((1 + (5 ** 0.5)) / 2)**int(n)
b = ((1 - (5 ** 0.5)) / 2)**int(n)
return round((a - b) / (5 ** 0.5))
Cependant, en raison de la valeur relativement faible de b, nous pouvons l’ignorer et la fonction peut être aussi simple que
def fib(n):
return round((((1+(5**0.5))/2)**int(n))/(5**0.5))
Voici une solution optimisée avec le dictionnaire
def Fibonacci(n):
if n<2 : return n
Elif not n in fib_dict :
fib_dict[n]= Fibonacci(n-1) + Fibonacci(n-2)
return fib_dict[n]
#dictionary which store Fibonacci values with the Key
fib_dict = {}
print(Fibonacci(440))
Étant donné le nombre de départ et le nombre maximum; Je pense que la solution suivante pour fibonacci serait intéressante. La bonne chose est qu'elle n'inclut pas la récursivité, réduisant ainsi la charge en mémoire.
# starting number is a
# largest number in the fibonacci sequence is b
def fibonacci(a,b):
fib_series = [a, a]
while sum(fib_series[-2:]) <=b:
next_fib = sum(fib_series[-2:])
fib_series.append(next_fib)
return fib_series
print('the fibonacci series for the range %s is %s'
%([3, 27], fibonacci(3, 27)))
the fibonacci series for the range [1, 12] is [3, 3, 6, 9, 15, 24]
Même si une réponse tardive mais cela pourrait être utile
fib_dict = {}
def fib(n):
try:
return fib_dict[n]
except:
if n<=1:
fib_dict[n] = n
return n
else:
fib_dict[n] = fib(n-1) + fib (n-2)
return fib(n-1) + fib (n-2)
print fib(100)
C'est beaucoup plus rapide que la manière traditionnelle
Alerte spoiler: ne lisez pas ceci si vous posez la question 2 du projet Euler tant que vous n’avez pas eu le temps de vous en occuper vous-même.
En dehors des approches basées sur la sommation de séries fermées, cela semble plus efficace que la plupart/tout ce que j'ai vu posté, car il ne nécessite qu'une seule itération de boucle plutôt bon marché par même nombre de Fibonacci; arriver à 4.000.000.
def sumOfEvenFibonacciNumbersUpTo(inclusiveLimit):
even = 0
next = 1
sum = 0
while even<=inclusiveLimit:
sum += even
even += next<<1
next = (even<<1)-next
return sum
Voici un simple sans récursion et O (n)
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a