web-dev-qa-db-ja.com

フィボナッチ数列、Python 3?

適切な関数構造で書くことに問題はないことを知っていますが、1行で最もPython的な方法でn番目のフィボナッチ数をどのように見つけることができるか知りたいのですが。

私はそのコードを書きましたが、私には最善の方法とは思えませんでした:

>>> fib=lambda n:reduce(lambda x,y:(x[0]+x[1],x[0]),[(1,1)]*(n-2))[0]
>>> fib(8)
13

どうすればもっと簡単にできるでしょうか?

43
utdemir
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.)

45
Jason S

めったに見られないトリックは、ラムダ関数がそれ自体を再帰的に参照できることです:

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
38
Mark Byers

最近、行列乗算を使用してフィボナッチ数を生成する方法を学びました。これはかなりクールでした。あなたは基本行列を取ります:

[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]
11
Chris Doggett

「最も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)

より効率的で、はるかに理解しやすい。

10
Voo

これは、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]
9
6502
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 ...

7
dalef

これは、整数演算を使用するフィボナッチシリーズの閉じた式であり、非常に効率的です。

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.LISPgenefib4に基づいています。私のもの(参照: http://paulhankin.github.io/Fibonacci/

7
Paul Hankin

別の例として、マーク・バイアーズの答えからの手がかり:

fib = lambda n,a=0,b=1: a if n<=0 else fib(n-1,b,a+b)
3
Jason S

以下は、再帰を使用せず、シーケンス履歴全体ではなく最後の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]
2
Jon Schoning

私は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()

結果は通常同じです。 enter image description here

再帰とキャッシュを備えたFiboslow_2、Fib整数演算、およびFibfastアルゴリズムが最適です。おそらく私のデコレータはパフォーマンスを測定するのに最適なものではありませんが、概観ではそれは良さそうでした。

2
tkircsi

私の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)
1
Adeel

_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の最初の部分である最後の反復から戻ります。
1
Xavier Guihot

この問題を解決するために、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]
1
MMSs
def fib(n):
    x =[0,1]
    for i in range(n):
        x=[x[1],x[0]+x[1]]
    return x[0]

ジェイソンSから手がかりをとってください。私のバージョンの方が理解が良いと思います。

1
bigtan

これが最も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]

上記のコードは、再帰を使用せず、値を格納するリストのみを使用します。

1
Edwin Clement

リスト内包表記を使用しないのはなぜですか?

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)]
1
Sanjurjo7
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)])

単一行のラムダフィボナッチですが、追加の変数がいくつかあります

0
BigChief

いくつかの値を含むリストを一度生成し、必要に応じて使用できます。

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]
0
Il Ilya

ここに私がそれをする方法がありますが、関数がリスト内包線の部分に対して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)
0
Ahmed Elbarky

論理演算子を使用したラムダ

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)]
0
sriharsha_bhat

類似:

    def fibonacci(n):
      f=[1]+[0]
      for i in range(n):
        f=[sum(f)] + f[:-1]
      print f[1]
0

再帰を使用した単純なフィボナッチ数ジェネレータ

_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個の数値で機能します。

0
Santosh Linkha