Fibonacci数字,在Python 3中有一个单行程?

时间:2011-02-08 17:01:34

标签: python fibonacci

我知道使用正确的函数结构编写没有任何问题,但我想知道如何用一行的大多数Pythonic方式找到第n个斐波那契数。

我写了那段代码,但这似乎不是我最好的方式:

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

怎么可能更好更简单?

23 个答案:

答案 0 :(得分:43)

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中;您需要在Python 3中执行from functools import reduce。)

答案 1 :(得分:36)

一个很少见的技巧是lambda函数可以递归地引用它自己:

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

答案 2 :(得分:12)

我最近学会了使用矩阵乘法生成Fibonacci数,这非常酷。你拿一个基本矩阵:

[1, 1]
[1, 0]

并将其自身乘以N次以得到:

[F(N+1), F(N)]
[F(N), F(N-1)]

今天早上,在淋浴墙上的蒸汽中涂鸦,我意识到你可以通过从第二个矩阵开始,将它的运行时间缩短一半,并将其自身乘以N / 2倍,然后使用N来选择一个指数从第一行/列开始。

稍微挤压,我把它归结为一行:

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]

答案 3 :(得分:10)

如果我们认为“最恐怖的方式”优雅而有效,那么:

def fib(nr):
    return int(((1 + math.sqrt(5)) / 2) ** nr / math.sqrt(5) + 0.5)

赢得胜利。为什么使用效率低下的算法(如果你开始使用memoization,我们可以忘记oneliner)当你可以通过用黄金比率逼近结果来解决O(1)中的问题?虽然实际上我显然是用这种形式写的:

def fib(nr):
    ratio = (1 + math.sqrt(5)) / 2
    return int(ratio ** nr / math.sqrt(5) + 0.5)

更高效,更容易理解。

答案 4 :(得分:9)

这是一个非递归(匿名)记忆一个班轮

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]

答案 5 :(得分:7)

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 ...

答案 6 :(得分:4)

这是使用整数运算的Fibonacci系列的闭合表达式,非常有效。

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个Fibonacci数)是O(n)位,该方法非常合理。

它基于来自http://fare.tunes.org/files/fun/fibonacci.lispgenefib4,而后者又基于我的一个效率较低的封闭式整数表达式(参见:http://paulhankin.github.io/Fibonacci/

答案 7 :(得分:3)

另一个例子,从Mark Byers的回答中得到启示:

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

答案 8 :(得分: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]

答案 9 :(得分:1)

我想看看是否可以创建整个序列,而不仅仅是最终值。

以下内容将生成一个长度为100的列表。它不包括前导[0, 1],并且适用于Python2和Python3。除了那条线之外没有其他线了!

(lambda i, x=[0,1]: [(x.append(x[y+1]+x[y]), x[y+1]+x[y])[1] for y in range(i)])(100)

输出

[1,
 2,
 3,
 ...
 218922995834555169026,
 354224848179261915075,
 573147844013817084101]

答案 10 :(得分:1)

我是Python新手,但出于学习目的做了一些措施。我收集了一些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算法似乎是最好的。也许我的装饰器不是衡量性能的最佳方法,但从总体上看似乎不错。

答案 11 :(得分: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)]

答案 12 :(得分:1)

我的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)

答案 13 :(得分:1)

我不知道这是否是最狡猾的方法,但这是我能想到的最好的方法: - &gt;

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]

上面的代码不使用递归,只是一个存储值的列表。

答案 14 :(得分:1)

def fib(n):
    x =[0,1]
    for i in range(n):
        x=[x[1],x[0]+x[1]]
    return x[0]

从Jason S那里得到提示,我认为我的版本有了更好的理解。

答案 15 :(得分:1)

为了解决这个问题,我在Stackoverflow Single Statement Fibonacci中得到了类似问题的启发,我得到了这个可以输出斐波那契序列列表的单行函数。虽然,这是一个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)

将此lambda函数分配给变量以重用它:

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]

答案 16 :(得分:0)

使用递归的简单Fibonacci数生成器

fib = lambda x: 1-x if x < 2 else  fib(x-1)+fib(x-2)
print fib(100)

这需要永远计算我计算机中的fib(100)

斐波纳契数也有closed form

fib = lambda n: int(1/sqrt(5)*((1+sqrt(5))**n-(1-sqrt(5))**n)/2**n)
print fib(50)

由于精度问题,这最多可以使用72个数字。

答案 17 :(得分:0)

带有逻辑运算符的Lambda

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

答案 18 :(得分:0)

这是我怎么做的,但是函数为列表理解行部分返回None,以允许我在里面插入一个循环.. 所以基本上它所做的就是在一个超过两个元素的列表中附加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)

答案 19 :(得分:0)

类似:

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

答案 20 :(得分:0)

您可以一次生成带有一些值的列表,并根据需要使用:

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]

答案 21 :(得分:0)

Python 3.8开始并引入assignment expressions (PEP 572):=运算符),我们可以在列表推导中使用和更新变量:

fib = lambda n,x=(0,1):[x := (x[1], sum(x)) for i in range(n+1)][-1][0]

此:

  • 将二元组n-1n-2启动为元组x=(0, 1)
  • 作为列表理解循环n的一部分,x通过赋值表达式x := (x[1], sum(x)))更新为新的n-1n-2
  • 最后,我们从上一次迭代中返回x的第一部分

答案 22 :(得分:0)

viewInCountry($slug)

单行lambda fibonacci,但有一些额外的变量