私は Project Euler 問題に取り組んでいます:フィボナッチ数の合計に関する問題です。
私のコード:
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]
問題の解決策は、sum(list2)を印刷することで簡単に見つけることができます。しかし、私が推測しているlist2を思い付くのに多くの時間がかかります。これを高速化する方法はありますか?または、この方法でも大丈夫ですか...
(問題:値が400万を超えないフィボナッチ数列の項を考慮することにより、偶数値の項の合計を見つけます。)
はい。原始的な再帰的解法は、多くの時間を要します。これは、計算された各数値について、以前のすべての数値を複数回計算する必要があるためです。次の画像をご覧ください。
それはあなたの関数でFibonacci(5)
を計算することを表します。ご覧のとおり、Fibonacci(2)
の値を3回、Fibonacci(1)
の値を5回計算します。計算したい数字が大きくなるほど悪化します。
それを悪化させるのはevenさらに悪いことは、リストで計算する各フィボナッチ数では、スピードアップのためにあなたが知っている以前の数を使用しないことです計算–各数値を「ゼロから」計算します。
これを高速化するいくつかのオプションがあります。
最も簡単な方法は、フィボナッチ数のリストを必要な数まで作成することです。それを行う場合、「ボトムアップ」などを使用してビルドし、以前の番号を再利用して次の番号を作成できます。フィボナッチ数のリスト_[0, 1, 1, 2, 3]
_がある場合、そのリストの最後の2つの数を使用して次の数を作成できます。
このアプローチは次のようになります。
_>>> def fib_to(n):
... fibs = [0, 1]
... for i in range(2, n+1):
... fibs.append(fibs[-1] + fibs[-2])
... return fibs
...
_
その後、次のようにして最初の20個のフィボナッチ数を取得できます。
_>>> fib_to(20)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
_
または、最初の40個のリストから17番目のフィボナッチ数を取得するには、次のようにします。
_>>> fib_to(40)[17]
1597
_
より高速にする別の方法もありますが、少し複雑です。問題は、すでに計算した値を再計算することなので、代わりに、計算した値を辞書に保存し、再計算する前にその値から取得することを選択できます。これはmemoizationと呼ばれます。次のようになります。
_>>> 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]
_
これにより、大きなフィボナッチ数を簡単に計算できます。
_>>> fib(400)
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
_
これは、実際にはPython 3がこれを行うためのデコレータを含んでいる一般的なテクニックです。自動メモ化を紹介します!
_import functools
@functools.lru_cache(None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
_
これは前の関数とほとんど同じことをしますが、computed
をすべて_lru_cache
_デコレーターで処理します。
Mitchが提案する3番目の方法は、リストに中間値を保存せずにカウントアップすることです。想像できます
_>>> def fib(n):
... a, b = 0, 1
... for _ in range(n):
... a, b = b, a+b
... return a
_
フィボナッチ数のlistを作成することが目標である場合、これらの最後の2つの方法はお勧めしません。 fib_to(100)
は[fib(n) for n in range(101)]
よりもa lot速くなります。後者ではまだ問題が発生するためですリスト内の各数値をゼロから計算する。
これは非常に高速なアルゴリズムであり、n番目のフィボナッチ数を他の回答で示されている単純な反復アプローチよりもはるかに高速に見つけることができますが、非常に高度です:
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
複雑な数学についてはもう少し読むことができます こちら 。
Pythonは末尾再帰を最適化しません。したがって、ここで紹介するほとんどのソリューションは、n
が大きすぎる場合(そして、私が1000を意味する場合)Error: maximum recursion depth exceeded in comparison
で失敗します。
再帰の制限は増やすことができますが、オペレーティングシステムのスタックオーバーフローでPythonがクラッシュします。
fib_memo
/fib_local
とfib_lru
/fib_local_exc
のパフォーマンスの違いに注意してください。LRUキャッシュは実行時エラーを生成するため、はるかに遅く、完了さえしません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)
結果:
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
反復ソリューションは断然最速であり、n=100k
(0.162秒)であってもスタックを破損しません。実際には、中間のフィボナッチ数を返しません。
n
thの偶数フィボナッチ数を計算する場合は、次のような反復アプローチを適用できます。
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
または、途中ですべての偶数に興味がある場合は、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))
結果:
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
これは、フィボナッチ数が7ミリ秒以内の最初の100個であり、端末への印刷のオーバーヘッドを含みます(Windowsでは過小評価しやすい)。
fib(n) = fib(n-1)+fib(n-2)
という事実に基づいて、簡単な解決策は
def fib(n):
if (n <=1):
return(1)
else:
return(fib(n-1)+fib(n-2))
ただし、ここでの問題は、一部の値が複数回計算されるため、非常に効率が悪いことです。理由はこのスケッチで見ることができます:
基本的に、fib
関数の各再帰呼び出しは、それ自体の使用のために以前のすべてのフィボナッチ数を計算する必要があります。そのため、@ kqrの回答で示されるツリーのすべてのリーフノードに表示される必要があるため、最も計算された値はfib(1)になります。このアルゴリズムの複雑さは、ツリーのノード数であり、$ O(2 ^ n)$です。
現在、より良い方法は、現在の値と前の値の2つの数値を追跡することです。そのため、各呼び出しで前の値をすべて計算する必要はありません。これはスケッチの2番目のアルゴリズムであり、次のように実装できます。
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)
このアルゴリズムの複雑さは線形$ O(n)$であり、いくつかの例は次のようになります。
>>> fib(1)
(1, 1)
>>> fib(2)
(1, 2)
>>> fib(4)
(3, 5)
>>> fib(6)
(8, 13)
これは、ウィキペディアのフィボナッチ数に関する記事に基づいています。アイデアは、ループと再帰を避け、必要に応じて値を単純に計算することです。
数学のウィズではなく、数式の1つを選択してコードにレンダリングし、値が正しくなるまで調整しました。
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)
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)
kqr の解法nr 2は私のお気に入りです。
ただし、この特定のケースでは、リスト内包内の結果の呼び出し間のすべての計算が失われています。
list2 = [i for i in list1 if fib(i) % 2 == 0]
、私はさらに一歩進んで、次のようにループステップ間でそれをメモすることにしました:
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]
O(1)解決策
数式はBinetの数式とも呼ばれます( read more )
基本的に、次のようにpython
に記述できます。
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))
ただし、bの値は比較的低いため、これを無視して、関数を次のように単純にすることができます。
def fib(n):
return round((((1+(5**0.5))/2)**int(n))/(5**0.5))
フィボナッチを再帰的に計算することは、繰り返し行うよりも非効率的です。私の推奨事項は次のとおりです。
Fibonacci
クラスを反復子として作成し、インデックス内の各要素に対して個別に計算を行います。おそらく @memoize
デコレータ (および here )以前の計算をすべてキャッシュします。
お役に立てれば!
これは、数のフィボナッチを一度だけ計算するフィボナッチの改良版です。
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)
ここでは、各番号のフィボナッチを辞書に保存しています。したがって、各反復で1回だけ計算され、フィボナッチ(10)では9回しか計算されていないことがわかります。
最初のn
偶数フィボナッチ数の合計を直接見つけるには、3n + 2
をお気に入りのメソッドに入れて、1つのフィボナッチ数を効率的に計算し、1ずつ減らして2で除算します(fib((3*n+2) - 1)/2)
)。 [〜#〜] oeis [〜#〜] ?以前、数学のダミーはどのように生き残ったのですか?
偶数フィボナッチ数の合計には、ニースの再帰式があります。フィボナッチ数の和のシーケンスのn番目の項は_S_{n} = 4*S_{n-1} + S_{n-2} + 2
_です。証明は読者に任されていますが、1)フィボ数も3つおきである、2)定義を使用した帰納法による上記の式の証明フィボ数の。 here のロジックを使用すると、少しの労力でこのための閉じた形式の式を導出できます。
S_{n} = -1/2 + (1/4 + 3*sqrt(5)/20)*(2+sqrt(5))**n + (1/4 - 3*sqrt(5)/20)*(2-sqrt(5))**n
sqrt
にもかかわらず、これは積分n
の積分であるため、以前の回答の便利な関数を使用して、またはsympy
などのパッケージを使用して簡単に計算できます。まさに根。
_import sympy as sp
one = sp.sympify(1) #to force casting to sympy types
k1 = -one/2
k2 = one/4 + 3*sp.sqrt(5)/20
k3 = one/4 - 3*sp.sqrt(5)/20
r1 = one
r2 = 2 + sp.sqrt(5)
r3 = 2 - sp.sqrt(5)
def even_sum_fibo(n):
#get the nth number in the sequence of even sums of Fibonacci numbers. If you want the sum of Fibos up to some number m, use n = m/3 (integer division)
return sp.simplify(k1*r1**n + k2*r2**n + k3*r3**n)
_
import time
def calculate_fibonacci_1(n):
if n == 0:
return 0
if n == 1:
return 1
return calculate_fibonacci_1(n - 1) + calculate_fibonacci_1(n - 2)
def calculate_fibonacci_2(n):
fib = [0] * n
fib[0] = 1
fib[1] = 1
for i in range(2, n):
fib[i] = fib[i - 1] + fib[i - 2]
return fib[n-1]
def calculate_fibonacci_3(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
def calculate_fibonacci_4(n):
v1, v2, v3 = 1, 1, 0
for rec in bin(n)[3:]:
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
def calculate_fibonacci_5(n):
if n == 0:
return (0, 1)
else:
a, b = calculate_fibonacci_5(n // 2)
c = a * (b * 2 - a)
d = a * a + b * b
if n % 2 == 0:
return (c, d)
else:
return (d, c + d)
n = 30
start = time.time()
calculate_fibonacci_1(n)
end = time.time()
print(end - start)
start = time.time()
calculate_fibonacci_2(n)
end = time.time()
print(end - start)
start = time.time()
calculate_fibonacci_3(n)
end = time.time()
print(end - start)
start = time.time()
calculate_fibonacci_4(n)
end = time.time()
print(end - start)
start = time.time()
calculate_fibonacci_5(n)
end = time.time()
print(end - start)
ために n=30
:
0.264876127243
6.19888305664e-06
8.10623168945e-06
7.15255737305e-06
4.05311584473e-06
ために n=300
:
>10s
3.19480895996e-05
1.78813934326e-05
7.15255737305e-06
6.19888305664e-06
ために n=3000
:
>10s
0.000766038894653
0.000277996063232
1.78813934326e-05
1.28746032715e-05
ために n=30000
:
>10s
0.0550990104675
0.0153529644012
0.000290870666504
0.000216007232666
ために n=300000
:
>10s
3.35211610794
0.979753017426
0.012097120285
0.00845909118652
ために n=3000000
:
>10s
>10s
>10s
0.466345071793
0.355515003204
ために n=30000000
:
>100s
>100s
>100s
16.4943139553
12.6505448818
免責事項:機能のコード4と5は私が書いたものではありません
Haskell 1ライナー:-
fibs = 0 : (f 1 1) where f a b = a : f b (a+b)
このコードは非常に効率的で、フィボナッチ数を最大(10^1000
)1秒以内に!このコードは、Project Eulerの この問題 にも役立ちます。
浮動小数点演算を使用しない場合は、平方根を含む方程式を使用してこれを計算できますが、係数を他の方法で追跡します。これにより、フィボナッチ数に対して本質的に一定の正確な時間アルゴリズムが得られます。
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
多くの再帰レベルがある場合、このような問題の実行には長い時間がかかります。再帰的な定義は、問題を簡単に理解できる方法でコーディングするのに適していますが、より高速に実行する必要がある場合は、 this thread の答えなどの反復ソリューションがはるかに高速になります。
1つの高速な方法は、fib(n/2)数を再帰的に計算することです。
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
辞書を使用した最適化されたソリューションを次に示します
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))
開始番号と最大番号を指定します。フィボナッチの次の解決策は興味深いと思います。良い点は、再帰が含まれていないことです。したがって、メモリの負荷が軽減されます。
# 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]
遅い回答ですが、役に立つかもしれません
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)
これは、従来の方法よりもはるかに高速です
ネタバレ注意:Project Euler Question 2を実行している場合は、自分でクラックするまで読んではいけません。
閉形式のシリーズ合計ベースのアプローチはさておき、これは、evenフィボナッチ数なので、4,000,000に達するのは12回だけです。
def sumOfEvenFibonacciNumbersUpTo(inclusiveLimit):
even = 0
next = 1
sum = 0
while even<=inclusiveLimit:
sum += even
even += next<<1
next = (even<<1)-next
return sum
これは、再帰とO(n)のない単純なものです
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a