高效计算斐波那契数列

Posted

技术标签:

【中文标题】高效计算斐波那契数列【英文标题】:Efficient calculation of Fibonacci series 【发布时间】:2013-08-12 21:28:39 【问题描述】:

我正在研究一个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 需要花费很多时间。有什么办法可以让这更快吗?还是这样也行……

(问题:考虑斐波那契数列中值不超过四百万的项,求偶数项之和。)

【问题讨论】:

P.S.我通过尝试找到了它不超过400万的值。 提示:尝试阅读 wiki 页面... 否:斐波那契数列的 wiki 页面...... 朴素递归O(phi^n)中运行 Project Euler 的 Even Fibonacci numbers 大约是 even-valued terms,而不是 具有偶数序数/偶数参数/偶数索引的值。如果您可以找出小于边界的最大项的序数(four million 带有“问题 2”),您可以在斐波那契函数的单个评估中找到该总和 【参考方案1】:

如果有很多级别的递归,任何这样的问题都需要很长时间才能运行。递归定义有助于以易于理解的方式对问题进行编码,但如果您需要它运行得更快,则使用迭代解决方案(例如 this thread 中的答案)会快得多。

【讨论】:

这就是为什么我建议海报查看斐波那契数列的 wiki 页面 递归表达某些东西并不会自动使其更容易理解 @Esailija 我同意它不会自动使它更容易理解,但是您通常可以更简洁地表达它,并且以与您看到显示公式的方式非常相似的方式表达它,例如斐波那契公式是 F_n = F_n-1 + F_n-2 种子值为 F_0 = 0, F_1 = 1。OP 建议的程序几乎相同。 @MitchWheat 如果您提供链接可能会有所帮助。我尝试搜索并找到了这个页面:***.com/tags/fibonacci/info,它似乎没有说任何未包含在 OP 中的内容。 @ChrisProsser:我假设即使是新用户也可以使用搜索引擎。【参考方案2】:

递归计算斐波那契比迭代计算效率最低。我的建议是:

花点时间创建一个Fibonacci 类作为迭代器,并为索引中的每个元素独立进行计算,可能使用一些@memoize decorator(还有here)来缓存所有以前的计算。

希望这会有所帮助!

【讨论】:

如果你在说“优化右递归代码”时指的是尾调用优化——这不是一个可能的优化,因为你递归了两个分支。如果有可能,您将能够使用关键字参数在 Python 中模拟它。 @kqr:我明白了,所以这种优化不能用函数式语言来做? 不是在使用这种方法计算斐波那契数时,不是。计算机需要保留堆栈中的每一帧才能执行加法。 @kqr:谢谢,我会从答案中删除该建议以防止进一步误导。【参考方案3】:

是的。原始递归解决方案需要很多时间。这样做的原因是,对于每个计算的数字,它需要多次计算之前的所有数字。请看下图。

它代表用你的函数计算Fibonacci(5)。如您所见,它计算了 Fibonacci(2) 的值 3 次,Fibonacci(1) 的值 5 次。您要计算的数字越大,情况就越糟。

使它甚至更糟糕的是,对于您在列表中计算的每个斐波那契数,您不会使用您所知道的以前的数字来加速计算 - 您计算每个数字“从头开始。”

有几个选项可以加快速度:


1。 “自下而上”创建列表

最简单的方法是创建一个斐波那契数字列表,直到您想要的数字。如果你这样做,你可以“自下而上”构建,并且可以重复使用以前的数字来创建下一个。如果您有斐波那契数列[0, 1, 1, 2, 3],您可以使用该列表中的最后两个数来创建下一个数。

这种方法看起来像这样:

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

2。记忆(相对先进的技术)

存在另一种使其更快的替代方法,但它也有点复杂。由于您的问题是您重新计算已经计算的值,因此您可以选择将已经计算的值保存在 dict 中,并在重新计算之前尝试从中获取它们。这称为记忆化。它可能看起来像这样:

>>> 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 装饰器处理。


3。数一数(一个天真的迭代解决方案)

第三种方法,如 Mitch 所建议的那样,是只计数而不将中间值保存在列表中。你可以想象做

>>> def fib(n):
...     a, b = 0, 1
...     for _ in range(n):
...         a, b = b, a+b
...     return a

如果您的目标是创建斐波那契数列列表,我不推荐最后两种方法。 fib_to(100) 将比[fib(n) for n in range(101)]很多,因为使用后者,您仍然会遇到从头计算列表中每个数字的问题。

【讨论】:

如果您将最后的函数从 mitch 更改为生成器,那会更好,因为您不会每次都重新计算数字。只需将 return 更改为 yield 并将其移至 for 循环。 @will 到时候基本不会成为第一个函数吗? (除了只能取一次值,不能索引) 很棒的回复!非常感谢。我也学到了很多新东西:D @kqr 是的。这将是相同的,但不需要将它们全部存储。如果你想索引它,那么你只需要做list(fib(N))。可能会受到很小的性能影响。我没有阅读完整的答案。我饿了。 memoization 将返回大集合in fib computed[n] = fib(n-1, computed) + fib(n-2, computed) [Previous line repeated 995 more times] RecursionError: maximum recursion depth exceeded【参考方案4】:

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]

【讨论】:

【参考方案5】:
int count=0;
void fibbo(int,int);

void main()



fibbo(0,1);

    getch();


void fibbo(int a,int b)



 count++;

 printf(" %d ",a);

if(count<=10)

     fibbo(b,a+b);

else

      return;


【讨论】:

你能写一个关于你的代码在做什么的小解释吗?【参考方案6】:

这是一个非常快的算法,它可以比其他答案中提出的简单迭代方法更快地找到第 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

您可以阅读更多有关所涉及数学的信息here。


【讨论】:

我在哪里可以找到第一种方法的数学解释来源? 您可以在此处阅读有关数学的信息:en.wikipedia.org/wiki/Fibonacci_number#Matrix_form。我的算法使用快速求幂将矩阵提升到 n 次方。 太神秘了,无法理解。我不建议新手使用该解决方案。 它比普通的封闭形式快吗? en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding @leitasat 可能,但对于n 的大值也会不正确,因为 python floats 的精度有限,与 ints 不同【参考方案7】:

R 中的解决方案,基准在 1.9 秒内计算出 1 到 1000 个斐波那契数列。在 C++ 或 Fortran 中会快得多,事实上,自从写了第一篇文章以来,我用 C++ 编写了一个等效函数,它在 0.0033 秒内完成,甚至 python 在 0.3 秒内完成。

#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 

【讨论】:

【参考方案8】:

一种快速的方法是递归计算 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

【讨论】:

【参考方案9】:

Haskell 1 班轮:-

fibs = 0 : (f 1 1) where f a b = a : f b (a+b)

此代码非常高效,可以在不到一秒的时间内计算出高达 (10^1000) 的斐波那契数! 此代码对于 Project Euler 中的 this problem 也很有用。

【讨论】:

问题被标记为 [python]【参考方案10】:

要直接找到第一个 n 偶数斐波那契数的总和,请将 3n + 2 放入您最喜欢的方法中,以有效地计算单个斐波那契数,减一除以二 (fib((3*n+2) - 1)/2))。在OEIS 之前,数学傻瓜是如何生存的?

【讨论】:

【参考方案11】:

Python 不会优化尾递归,因此如果 n 太大(我的意思是 1000),这里介绍的大多数解决方案都会失败并显示 Error: maximum recursion depth exceeded in comparison

可以增加递归限制,但会导致Python在操作系统中堆栈溢出时崩溃。

请注意fib_memo / fib_localfib_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 秒)也不会破坏堆栈。它确实不返回中间斐波那契数。

如果您想计算nth 偶数斐波那契数,您可以采用这样的迭代方法:

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

或者,如果您对途中的每个偶数感兴趣,请使用生成器

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 上很容易低估)。

【讨论】:

+1 用于向这个问题介绍 [generator]。 (您可以使用a, b = 0, 2a, b = b, a + 4*b 直接生成偶值项。) 我用Ruby做了一个简单的例子(n - 1).times.reduce([0, 1]) |array| [array[1], array[0] + array[1]] .last @greybeard:谢谢,这对于 n = 100k 有很大的不同(12.5s 与 0.6s,禁用打印到控制台)。【参考方案12】:

虽然回答较晚,但可能会有所帮助

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)

这比传统方式快很多

【讨论】:

回答什么?试着理解这个问题,检查你想要给出的答案是否已经存在 - 或者在“重复”之一中。 @greybeard 它只是一个不会伤害任何人的附加信息。它可能对您没有帮助,但它肯定会帮助其他寻求它的人。【参考方案13】:

基于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 函数的递归调用都必须计算所有先前的斐波那契数以供自己使用。因此,计算最多的值将是 fib(1),因为它必须出现在 @kqr 的答案所示的树的所有叶节点中。这个算法的复杂度是树的节点数,也就是$O(2^n)$。

现在更好的方法是跟踪两个数字,当前值和之前的值,这样每次调用就不必计算所有之前的值。这是草图中的第二个算法,可以如下实现

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)

【讨论】:

【参考方案14】:

我基于 Wikipedia 上关于斐波那契数列的一篇文章。这个想法是避免循环和递归,并根据需要简单地计算值。

不是数学天才,选择其中一个公式并将其渲染为代码并对其进行调整,直到值正确。

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)

【讨论】:

这如何使[finding] the sum of the even-valued terms not [exceeding] four million 更快? getFib(10000) OverflowError: complex exponentiation【参考方案15】:

如果您不使用浮点运算,则可以使用具有平方根的方程来计算它,但在进行时以其他方式跟踪系数。这给出了一个O(log n) 算术运算(与用于记忆的O(n log n) 运算相反)算法。

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 __name__ == "__main__":
    assert rootipower(1,2,3,3) == (37,30) # 1+2sqrt(3) **3 => 13 + 4sqrt(3) => 39 + 30sqrt(3)
    assert fib(10)==55

【讨论】:

这不是“基本上恒定的时间”;求幂(你的 rootipower 函数)执行 ~lg n 算术运算,并且输出本身有 ~n 位,因此没有算法可以是次线性的(参见 the answer by yairchu here)【参考方案16】:

这是斐波那契的一些改进版本,我们只计算一次数字的斐波那契:

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)

在这里,我们将每个数字的斐波那契存储在字典中。所以你可以看到它每次迭代只计算一次,而 Fibonacci(10) 只计算 9 次。

【讨论】:

【参考方案17】:

O(1) 解

事实证明,偶数斐波那契数的和有一个很好的递归公式。偶数斐波那契数之和序列中的第 n 项是S_n = 4*S_n-1 + S_n-2 + 2 证明留给读者,但涉及证明 1) 偶数斐波那契数是每三分之一,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)

【讨论】:

【参考方案18】:

有一个 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)

【讨论】:

math.pow(x, N) 最多不是 O(1),O(log(N))。 想解释一下吗? 复杂度为 O(1) 当且仅当程序在相同数量的 CPU 周期内完成,而不管输入如何。 math.pow(2, N) 不是单个 CPU 指令,如果使用快速求幂,它会转换为大量乘法 (log(N))。 python 中的整数乘法也不是常数时间——它们可以是任意大小(例如 1024 位),因此您需要多个 CPU 指令来乘以大整数。但是,在您的情况下,您使用浮点数,这些浮点数是恒定的 64 位,因此复杂度为 O(log(N))。请注意,您从中得到的结果只是一个浮点近似值。【参考方案19】:

给定起始编号和最大编号;我认为斐波那契的以下解决方案会很有趣。好处是它不包含递归 - 从而减少了内存负担。

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

【讨论】:

【参考方案20】:

这是一个简单的,没有递归和 O(n)

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

【讨论】:

这回答了什么问题? @greybeard "有什么办法可以加快速度吗?"【参考方案21】:

剧透警告:如果您正在做 Project Euler 问题 2,请不要阅读本文,除非您自己对此有所了解。

撇开基于闭式系列求和的方法不谈,这似乎比我所看到的大多数/所有内容更有效,因为它只需要一个相当便宜的循环迭代每个偶数斐波那契数,因此只需 12 次迭代即可达到 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

【讨论】:

我猜你可以自己检查。 让我们澄清 sumOfEvenFibonacciNumbersUpTo 函数的意图。斐波那契数列是 0、1、1、2、3、5、8 等。此函数旨在返回(例如)0、0、2、2、2、2、2、2、10、10、10对于从 0 到 10 的 inclusiveLimit 输入,即它按照它所说的那样做。特别是它不像大多数答案那样打印斐波那契数列。它直接执行 OP 要求的操作,即计算序列中小于或等于限制参数的偶数元素的总和。需要少量数学来证明它有效。 我很遗憾有人否决了这个答案。这让我怀疑我是否愿意在这里分享信息。 如果有人想观看这个工作,请将print(even, next, sum) 添加到循环中。 (如果您解释了它的工作原理/原因,有人可能会奖励赏金。)【参考方案22】:

这是一个使用字典的优化解决方案

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

【讨论】:

【参考方案23】:

O(1) 解决方案

该公式也称为比奈公式 (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))

【讨论】:

fib(10000) OverflowError: (34, '结果太大') 这似乎只是一个近似的解决方案。例如 fib(1000) 的结果是错误的。 在 Python3 中,差异发生在 n=72 和更高的位置。作为条件 ndecimal 包来提高浮点精度,则可以使用此代码获得更大的斐波那契数。 我的算法可以做到至少 Fib(1亿)无误。【参考方案24】:
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不是我写的

【讨论】:

这回答了什么问题?我没有看到 快速解决 Project Euler Problem 2【参考方案25】:

这比上面的一切都快

from sympy import fibonacci
%timeit fibonacci(10000)

262 ns ± 10.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

【讨论】:

【参考方案26】:

这里还有一些公式,来自OEIS:

F(n) = ((1+sqrt(5))^n - (1-sqrt(5))^n)/(2^n*sqrt(5)) 或者,F(n) = ((1/2+sqrt(5)/2)^n - (1/2-sqrt(5)/2)^n)/sqrt(5) F(n) = 圆形(phi^n/sqrt(5));其中 phi 为 (1 + sqrt(5)) / 2 F(n+1) = Sum_j=0..floor(n/2) 二项式(n-j, j)

其中一些公式在上述其他 cmets 中有实现。

【讨论】:

这回答了什么问题?我不太完全看到解决Project Euler Problem 2快【参考方案27】:

另一种快速解决方案:

def fibonnaci(n):
    a = []  
    while n != 1: 
        a.append(n&1)
        n >>= 1
    f1 = 1
    f2 = 1
    while a:
        t = f1 * (f2 * 2 - f1)
        f2 = f2 * f2 + f1 * f1
        if a.pop() is 1:
            f1 = f2
            f2 += t
        else:
            f1 = t
    return f1

【讨论】:

【参考方案28】:

Project Euler 是练习编码的好地方。

来回答你的问题...

斐波那契数列;将最后一个数字之前的数字与最后一个数字相加,得到的总和就是系列中的新数字。

您定义了一个函数,但最好使用 while 循环。

i = 0
x = [0,1,2]
y =[]
n = int(input('upper limit of fibonacci number is '))
while i< n:
    z= x[-1]+x[-2]
    x.append(z)
    if z%2 == 0:
        y.append(z)
    i = x[-1]
    print(i)
print('The sum of even fibbunacci num in given fibonacci number is ',sum(y)+2)

【讨论】:

you defined a function but it would be best to use a while loop 不排除另一个。 计算一个斐波那契数而言,我发现 kqr in method 3(2015)(在 2018 年被 dan-klasson 重复)更好,如果可悲的是 undocumented . @greybeard,我的意思是问题中定义的函数并不理想,最好使用while循环,因为在问题中它是一个递归。(递归与循环再次取决于语言)而且这个问题还需要列出偶数斐波那契数列并总结一下,我认为答案(由 dan-klasson 在 2018 年重复)不适合这种情况。我仍在努力撰写答案,并感谢您对此的诚实意见。【参考方案29】:

我做了一些研究,发现了一个叫做比内公式的公式。 这个公式可以在O(1)时间内轻松计算出斐波那契数列的第n个数。

这是我翻译成 Python 的 Java 代码:

def fibonacci(n):
    five_sqrt = 5 ** 0.5

    return int(round((((1 + five_sqrt)/2) ** n)/five_sqrt))

for i in range(1, 21):
    print(fibonacci(i))

输出:

1, 1、 2、 3、 5、 8、 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765

【讨论】:

不,通常不是 O(1) 时间,因为您需要计算浮点数的巨大能力。如果您尝试使用比内公式、一支铅笔和大量纸来计算斐波那契数,您会很容易看到这一点!【参考方案30】:

我知道这个问题是 8 年前提出的,并且已经得到了彻底的回答……很抱歉将其重新回到顶部。但总有更多话要说。我在搜索以改进我自己的算法时遇到了这个问题,我想分享一下。

我想提供我自己的看法,因为我发现这并没有真正被提出。我认为我的算法在迄今为止的贡献者中是独一无二的。我利用众所周知的斐波那契数方程(***)来缩小索引。其他一两个简要介绍了一个基本版本,但我更进一步。

递归算法,但我能够在 0.15 秒内计算出 Fib(200 万),在 2 秒内计算出 Fib(1000 万),在 75 秒内计算出 Fib(1 亿)秒。都没有错误。我会这么说,它不是计算整个连续斐波那契数列最快的;这最适合挑选出非常大的个体。

到目前为止提到的大多数算法——无论它们有多快——都很难在没有递归深度问题的情况下超过 Fib(100)。一些贡献者已经避开了我的部分算法,尽管他们有一些我没有的缺点。不是说最好的或任何东西,但我认为它非常快并且可以计算出非常大的谎言。我认为值得加入讨论。

最重要的是,我不使用任何内存。没有任何类型的列表、字典或数组。没有缓存或记忆。甚至没有一个持久保存的常量。没有特殊的包导入。只是基本的、普通的、具有基本整数类型的python。我还扩展了函数以计算对运行时间的影响可以忽略不计的负数。

不过我应该警告一下……我是数学家,而不是程序员。我毫不怀疑这可以进一步改进。而且我不知道大 O 是什么。

def fib(n):
      if n<0: return int(pow(-1, (n&1)+1))*fib(-n)
      if n == 0: return 0
      if n==1 or n==2: return 1
      if n==3: return 2
      
      # n is multiple of 3
      if n%3 == 0:
            third = n//3
            fibthird = fib(third)
            return 5*pow(fibthird,3) + 3*pow(-1, third)*fibthird
            
      # even n
      if n&1==0:
            return pow(fib((n>>1) + 1),2) - pow(fib((n>>1) - 1), 2)

      # for odd n
      return ( pow(fib((n>>1)+1),2) + pow(fib(n>>1),2) )

运行代码,告诉我你的想法。我很想听听社区的意见。我个人对它印象深刻,并且已经运行了一段时间。不过,在我有限的(编程)知识中找不到改进它的方法。尝试添加列表、记忆、缓存等,要么无法改进任何东西,要么使运行时变得更糟。在极少数情况下,我发现一些可以改善运行时的东西,运行时的好处可以忽略不计,内存成本很高,我认为这是不公平的交易。


主要测试

为了增加乐趣,我在下面包含了一个与斐波那契数相关的基本概率 is_prime 测试:

def is_prime_fib(n):
      # Fibonacci Probabilistic is_prime test. Compositeness deterministic.
      if n==1: return False
      if n==5: return True
      if n%5 in [1,4] and fib(n-1) % n == 0: return True
      if n%5 in [2,3] and fib(n+1) % n == 0: return True
      return False

就其本身而言,斐波那契素数检验是概率性的。 n=1 和 n=5 的情况是无法产生正确结果的奇怪情况,但它们太明显了,无需担心。除此之外,False 在复合性中是确定性的,True 在素数中是概率性的。通过该测试为真的复合是斐波那契伪素数。

【讨论】:

这增加了对“减半/加倍”方法的回避,我猜计算速度大约是计算 一个 较小的值并采取更大的步骤。 我希望你能把 prime testing 排除在外 - 我建议你发布一个单独的(交叉链接的)self-answered question(我不喜欢 answer寻求问题,而不是寻求急需问题的解决方案)。 我包含素数测试是因为它是一个常见问题,并且大素数需要大斐波那契,该算法能够生成。诚然,这是一个旁注。但是还有什么其他原因必须生成这么大的斐波那契呢?至于您只看到语法或其他表面变化的批评,您显然没有运行代码或观察到性能,并且您显然不关心我的主张足以认为它们足够有效以进行测试。你真的不认为能够生成fib(1亿)的算法在这个领域竞争吗? 我注意到 en.wikipedia 也偏离了斐波那契数页上的素数测试。 大部分内容都是done before,如果不是每个人都使用每种语言的话。

以上是关于高效计算斐波那契数列的主要内容,如果未能解决你的问题,请参考以下文章

递归优化的斐波那契数列

斐波那契数列

用递归法计算斐波那契数列的第n项

08《算法入门教程》递归算法之斐波那契数列

编写一递归函数求斐波那契数列的前40项

Go语言 斐波那契数列的解法