適切な関数構造で書くことに問題はないことを知っていますが、1行で最もPython的な方法でn番目のフィボナッチ数をどのように見つけることができるか知りたいのですが。
私はそのコードを書きましたが、私には最善の方法とは思えませんでした:
>>> fib=lambda n:reduce(lambda x,y:(x[0]+x[1],x[0]),[(1,1)]*(n-2))[0]
>>> fib(8)
13
どうすればもっと簡単にできるでしょうか?
fib = lambda n:reduce(lambda x,n:[x[1],x[0]+x[1]], range(n),[0,1])[0]
(これにより、[a、b]から[b、a + b]にマップされたタプルが維持され、[0,1]に初期化され、N回繰り返され、最初のタプル要素を取ります)
>>> fib(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051
89040387984007925516929592259308032263477520968962323987332247116164299644090653
3187938298969649928516003704476137795166849228875L
(この番号付けでは、fib(0)= 0、fib(1)= 1、fib(2)= 1、fib(3)= 2などに注意してください)
(また注意:reduce
はPython 2.7に組み込まれていますが、Python 3には組み込まれていません。実行する必要がありますfrom functools import reduce
in Python 3.)
めったに見られないトリックは、ラムダ関数がそれ自体を再帰的に参照できることです:
fib = lambda n: n if n < 2 else fib(n-1) + fib(n-2)
ちなみに、わかりにくいのでめったに見られず、この場合も非効率的です。複数行で記述する方がはるかに優れています。
def fibs():
a = 0
b = 1
while True:
yield a
a, b = b, a + b
最近、行列乗算を使用してフィボナッチ数を生成する方法を学びました。これはかなりクールでした。あなたは基本行列を取ります:
[1, 1]
[1, 0]
そしてそれをN回掛けると、次のようになります。
[F(N+1), F(N)]
[F(N), F(N-1)]
今朝、シャワーの壁の蒸気にだらだらしたので、2番目の行列から始めてそれ自体をN/2回乗算し、次にNを使用して最初の行列からインデックスを選択することで、実行時間を半分にできることに気付きました。行/列。
少し絞って、1行にしました。
import numpy
def mm_fib(n):
return (numpy.matrix([[2,1],[1,1]])**(n//2))[0,(n+1)%2]
>>> [mm_fib(i) for i in range(20)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
「最もPython的な方法」がエレガントで効果的であると考えると、次のようになります。
def fib(nr):
return int(((1 + math.sqrt(5)) / 2) ** nr / math.sqrt(5) + 0.5)
勝つ手はありません。 O(1)で結果を黄金比で近似することにより問題をうまく解決できるのに、なぜ非効率的なアルゴリズムを使用します(メモ化を使い始めると、ワンライナーを忘れることができます)?実際には私は明らかにこの形でそれを書きます:
def fib(nr):
ratio = (1 + math.sqrt(5)) / 2
return int(ratio ** nr / math.sqrt(5) + 0.5)
より効率的で、はるかに理解しやすい。
これは、1つのライナーを記念する非再帰的(匿名)です。
fib = lambda x,y=[1,1]:([(y.append(y[-1]+y[-2]),y[-1])[1] for i in range(1+x-len(y))],y[x])[1]
fib = lambda n, x=0, y=1 : x if not n else fib(n-1, y, x+y)
実行時O(n)、fib(0)= 0、fib(1)= 1、fib(2)= 1 ...
これは、整数演算を使用するフィボナッチシリーズの閉じた式であり、非常に効率的です。
fib = lambda n:pow(2<<n,n+1,(4<<2*n)-(2<<n)-1)%(2<<n)
>> fib(1000)
4346655768693745643568852767504062580256466051737178
0402481729089536555417949051890403879840079255169295
9225930803226347752096896232398733224711616429964409
06533187938298969649928516003704476137795166849228875L
O(log n)算術演算で結果を計算し、それぞれがO(n)ビットの整数に作用します。結果(n番目のフィボナッチ数)がO(n)ビット、メソッドはかなり合理的です。
http://fare.tunes.org/files/fun/fibonacci.LISP のgenefib4
に基づいています。私のもの(参照: http://paulhankin.github.io/Fibonacci/ )
別の例として、マーク・バイアーズの答えからの手がかり:
fib = lambda n,a=0,b=1: a if n<=0 else fib(n-1,b,a+b)
以下は、再帰を使用せず、シーケンス履歴全体ではなく最後の2つの値のみをメモする実装です。
以下のnthfib()は、元の問題の直接的な解決策です(インポートが許可されている限り)
上記のReduceメソッドを使用するよりもエレガントではありませんが、要求されたものとは少し異なりますが、シーケンスをn番目の数まで出力する必要がある場合は、無限ジェネレーターとしてより効率的に使用できるようになります(下記のfibgen()のように少し書き直しました。
from itertools import imap, islice, repeat
nthfib = lambda n: next(islice((lambda x=[0, 1]: imap((lambda x: (lambda setx=x.__setitem__, x0_temp=x[0]: (x[1], setx(0, x[1]), setx(1, x0_temp+x[1]))[0])()), repeat(x)))(), n-1, None))
>>> nthfib(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051
89040387984007925516929592259308032263477520968962323987332247116164299644090653
3187938298969649928516003704476137795166849228875L
from itertools import imap, islice, repeat
fibgen = lambda:(lambda x=[0,1]: imap((lambda x: (lambda setx=x.__setitem__, x0_temp=x[0]: (x[1], setx(0, x[1]), setx(1, x0_temp+x[1]))[0])()), repeat(x)))()
>>> list(islice(fibgen(),12))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
私はPython newcomerですが、学習目的でいくつかの測定を行いました。いくつかのfiboアルゴリズムを収集し、いくつかの測定を行いました。
from datetime import datetime
import matplotlib.pyplot as plt
from functools import wraps
from functools import reduce
from functools import lru_cache
import numpy
def time_it(f):
@wraps(f)
def wrapper(*args, **kwargs):
start_time = datetime.now()
f(*args, **kwargs)
end_time = datetime.now()
elapsed = end_time - start_time
elapsed = elapsed.microseconds
return elapsed
return wrapper
@time_it
def fibslow(n):
if n <= 1:
return n
else:
return fibslow(n-1) + fibslow(n-2)
@time_it
@lru_cache(maxsize=10)
def fibslow_2(n):
if n <= 1:
return n
else:
return fibslow_2(n-1) + fibslow_2(n-2)
@time_it
def fibfast(n):
if n <= 1:
return n
a, b = 0, 1
for i in range(1, n+1):
a, b = b, a + b
return a
@time_it
def fib_reduce(n):
return reduce(lambda x, n: [x[1], x[0]+x[1]], range(n), [0, 1])[0]
@time_it
def mm_fib(n):
return (numpy.matrix([[2, 1], [1, 1]])**(n//2))[0, (n+1) % 2]
@time_it
def fib_ia(n):
return pow(2 << n, n+1, (4 << 2 * n) - (2 << n)-1) % (2 << n)
if __name__ == '__main__':
X = range(1, 200)
# fibslow_times = [fibslow(i) for i in X]
fibslow_2_times = [fibslow_2(i) for i in X]
fibfast_times = [fibfast(i) for i in X]
fib_reduce_times = [fib_reduce(i) for i in X]
fib_mm_times = [mm_fib(i) for i in X]
fib_ia_times = [fib_ia(i) for i in X]
# print(fibslow_times)
# print(fibfast_times)
# print(fib_reduce_times)
plt.figure()
# plt.plot(X, fibslow_times, label='Slow Fib')
plt.plot(X, fibslow_2_times, label='Slow Fib w cache')
plt.plot(X, fibfast_times, label='Fast Fib')
plt.plot(X, fib_reduce_times, label='Reduce Fib')
plt.plot(X, fib_mm_times, label='Numpy Fib')
plt.plot(X, fib_ia_times, label='Fib ia')
plt.xlabel('n')
plt.ylabel('time (microseconds)')
plt.legend()
plt.show()
再帰とキャッシュを備えたFiboslow_2、Fib整数演算、およびFibfastアルゴリズムが最適です。おそらく私のデコレータはパフォーマンスを測定するのに最適なものではありませんが、概観ではそれは良さそうでした。
私の2セント
# One Liner
def nthfibonacci(n):
return long(((((1+5**.5)/2)**n)-(((1-5**.5)/2)**n))/5**.5)
OR
# Steps
def nthfibonacci(nth):
sq5 = 5**.5
phi1 = (1+sq5)/2
phi2 = -1 * (phi1 -1)
n1 = phi1**(nth+1)
n2 = phi2**(nth+1)
return long((n1 - n2)/sq5)
_Python 3.8
_の開始、および 代入式(PEP 572) (_:=
_演算子)の導入により、リスト内包内の変数を使用および更新できます。
_fib = lambda n,x=(0,1):[x := (x[1], sum(x)) for i in range(n+1)][-1][0]
_
この:
n-1
_および_n-2
_をタプルとして開始しますx=(0, 1)
n
回、x
は割り当て式(x := (x[1], sum(x))
)を新しい_n-1
_および_n-2
_値にx
の最初の部分である最後の反復から戻ります。この問題を解決するために、Stackoverflow Single Statement Fibonacci で同様の質問に触発され、フィボナッチシーケンスのリストを出力できるこの1行の関数を取得しました。ただし、これはPython 2スクリプトであり、Python 3ではテストされていません。
(lambda n, fib=[0,1]: fib[:n]+[fib.append(fib[-1] + fib[-2]) or fib[-1] for i in range(n-len(fib))])(10)
このラムダ関数を変数に割り当てて再利用します。
fib = (lambda n, fib=[0,1]: fib[:n]+[fib.append(fib[-1] + fib[-2]) or fib[-1] for i in range(n-len(fib))])
fib(10)
出力はフィボナッチ数列のリストです:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
def fib(n):
x =[0,1]
for i in range(n):
x=[x[1],x[0]+x[1]]
return x[0]
ジェイソンSから手がかりをとってください。私のバージョンの方が理解が良いと思います。
これが最もPython的な方法であるかどうかはわかりませんが、これが私が思いつく最高の方法です:->
Fibonacci = lambda x,y=[1,1]:[1]*x if (x<2) else ([y.append(y[q-1] + y[q-2]) for q in range(2,x)],y)[1]
上記のコードは、再帰を使用せず、値を格納するリストのみを使用します。
リスト内包表記を使用しないのはなぜですか?
from math import sqrt, floor
[floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))) for n in range(100)]
数学のインポートなしで、それほどきれいではありません:
[int(((1+(5**0.5))**n-(1-(5**0.5))**n)/(2**n*(5**0.5))) for n in range(100)]
import math
sqrt_five = math.sqrt(5)
phi = (1 + sqrt_five) / 2
fib = lambda n : int(round(pow(phi, n) / sqrt_five))
print([fib(i) for i in range(1, 26)])
単一行のラムダフィボナッチですが、追加の変数がいくつかあります
いくつかの値を含むリストを一度生成し、必要に応じて使用できます。
fib_fix = []
fib = lambda x: 1 if x <=2 else fib_fix[x-3] if x-2 <= len(fib_fix) else (fib_fix.append(fib(x-2) + fib(x-1)) or fib_fix[-1])
fib_x = lambda x: [fib(n) for n in range(1,x+1)]
fib_100 = fib_x(100)
例より:
a = fib_fix[76]
ここに私がそれをする方法がありますが、関数がリスト内包線の部分に対してNoneを返し、内部にループを挿入できるようにします。つまり、基本的には、2つの要素を超えるリストの内部にfib seqの新しい要素を追加します
>>f=lambda list,x :print('The list must be of 2 or more') if len(list)<2 else [list.append(list[-1]+list[-2]) for i in range(x)]
>>a=[1,2]
>>f(a,7)
論理演算子を使用したラムダ
fibonacci_oneline = lambda n = 10, out = []: [ out.append(i) or i if i <= 1 else out.append(out[-1] + out[-2]) or out[-1] for i in range(n)]
類似:
def fibonacci(n):
f=[1]+[0]
for i in range(n):
f=[sum(f)] + f[:-1]
print f[1]
再帰を使用した単純なフィボナッチ数ジェネレータ
_fib = lambda x: 1-x if x < 2 else fib(x-1)+fib(x-2)
print fib(100)
_
これは私のコンピューターでfib(100)
を計算するのに永遠にかかります。
フィボナッチ数列の 閉じた形式 もあります。
_fib = lambda n: int(1/sqrt(5)*((1+sqrt(5))**n-(1-sqrt(5))**n)/2**n)
print fib(50)
_
精度の問題により、これは最大72個の数値で機能します。