Python 改进斐波那契数列递归后,计算第1000万项只需4秒

简介: Python 改进斐波那契数列递归后,计算第1000万项只需4秒

image.png



改进思路

上一篇《不自己试试,还真猜不出递归函数的时间复杂度》中写了一个递归函数斐波那契数列的某一项的值,号称它是终极改进了,但它有一个缺点:偶数项比奇数项算得慢。今天实然想到应该把偶数项也转换成奇数项来算,就会成倍提高运算速度:


原理是这样的:

   F(2n)=F(n)*(F(n+1)+F(n-1)) ; F(2n+1)=F²(n+1)+F²(n)


偶数项需要三个项递归计算,奇数项只有两个项要递归。但是想到另外有公式可以把偶数项转换成奇数项:


   ∵ F(2n)=F(2n+1)-F(2n-1)

   ∴ F(2n)=F²(n+1)+F²(n) - (F²(n)+F²(n-1))=F²(n+1)-F²(n-1)


之前版本的时间测试,奇数项比偶数项计算快:

>>> def Fib(n:int,n1=34,n2=55):
    if n<11: return [0,1,1,2,3,5,8,13,21,34,55][n]
    if n==11: return n1+n2
    if n<1001:return Fib(n-1,n2,n1+n2)
    t=n//2
    if n%2: return Fib(t+1)**2+Fib(t)**2
    return Fib(t)*(Fib(t+1)+Fib(t-1))
>>> for i in range(1,6):
   t=time();n=Fib(i*1000000);print(time()-t)
   t=time();n=Fib(i*1000000+1);print(time()-t)
7.499995231628418
5.33894419670105
19.355664014816284
15.778117656707764
37.201030254364014
25.430164575576782
>>> t=time();n=Fib(5000000);print(time()-t)
77.2153069972992
>>> t=time();n=Fib(5000000+1);print(time()-t)
53.111284017562866
>>>


改进一:偶数项改奇数项

>>> def Fib(n:int,n1=34,n2=55):
    if n<11: return [0,1,1,2,3,5,8,13,21,34,55][n]
    if n==11: return n1+n2
    if n<1001: return Fib(n-1,n2,n1+n2)
    t=n//2
    if n%2: return Fib(t+1)**2+Fib(t)**2
    return Fib(t+1)**2-Fib(t-1)**2
>>> from time import time
>>> for i in range(1,7):
   t=time();n=Fib(i*1000000);print(f'Fib({i*1000000})::{time()-t}')
   t=time();n=Fib(i*1000000+1);print(f'Fib({i*1000000+1})::{time()-t}')
Fib(1000000)::1.2499959468841553
Fib(1000001)::1.2343709468841553
Fib(2000000)::3.0624945163726807
Fib(2000001)::3.062493324279785
Fib(3000000)::5.203120470046997
Fib(3000001)::5.218744993209839
Fib(4000000)::7.812499523162842
Fib(4000001)::7.8281190395355225
Fib(5000000)::10.343748092651367
Fib(5000001)::10.343748331069946
Fib(6000000)::13.65624475479126
Fib(6000001)::13.671867370605469

用了新版公式后,不管奇数还是偶数项,耗时不再是起伏的而是单调递增且相邻的项用时基本相等,而且函数的运算速度也都成倍提高了。计算第500万项时,从原来的80秒减少到10秒了;计算第2000万项也只要80秒,而第1000万项只要半分钟就够了!

>>> from time import time
>>> t=time();n=Fib(10000000);print(time()-t)
27.9345383644104
>>> t=time();n=Fib(10000000+1);print(time()-t)
28.794012784957886
>>> t=time();n=Fib(20000000);print(time()-t)
77.22278428077698
>>> 



改进二:前面的项改用循环获取

把前面的项(比如前600项,具体定哪个值最优没有细测)用循环来获取,提速效果明显。

>>> def fib(n):
    if n<600:
        n1 = n2 = 1
        for _ in range(2,n):
            n1,n2 = n1+n2,n1
        return n1
    t = n//2
    if n%2: return fib(t+1)**2 + fib(t)**2
    else: return fib(t+1)**2 - fib(t-1)**2
>>> t=time();n=fib(10000000);print(time()-t)
3.8922224044799805
>>> t=time();n=fib(10000001);print(time()-t)
3.8972229957580566
>>> t=time();n=fib(20000000);print(time()-t)
11.089634418487549
>>> t=time();n=fib(50000000);print(time()-t)
45.387596130371094
>>> 

计算第5000万项:45秒;第2000万项:11秒;第1000万项:4秒钟!

--All done!


附录


斐波那契数列重要恒等式


数列通项公式:

F(1)=F(2)=1;

F(n)=F(n-1)+F(n-2), n>2.


通项公式的前一项是:

F(n-1)=F(n-2)+F(n-3)


前一项代入通项中,得:

F(n)=2*F(n-2)+F(n-3)

再把更前一项F(n-2)=F(n-3)+F(n-4)代入上式,得:


F(n)=3*F(n-3)+2*F(n-4)

...以此类推:

F(n)=5*F(n-4)+3*F(n-5)

F(n)=8*F(n-5)+5*F(n-6)

F(n)=13*F(n-6)+8*F(n-7)

......



等号右边的系数又分别都是斐波那契数列,

两个新数列的函数自变量刚好差1,

且F(7)=13,F(6)=8,F(5)=5...可得:

F(n)=F(m)F(n-m+1)+F(m-1)F(n-m) --公式①

先用m+n替换公式①中的n,得:

F(m+n)=F(m)*F(n+1)+F(m-1)*F(n) --公式②

令m=n,代入公式②,得:

F(2n)=F(n)*F(n+1)+F(n-1)*F(n) --公式③

再令m=n+1,代入公式②,得:

F(2n+1)=F(n+1)*F(n+1)+F(n)*F(n)

表示成平方和形式:

F(2n+1)=F²(n+1)+F²(n) --公式④

公式④自变量减一,得:

F(2n-1)=F²(n)+F²(n-1) --公式④'

公式④自变量增一,得:

F(2n+3)=F²(n+2)+F²(n+1)

上式与公式④相减:

F²(n+2)-F²(n)=F(2n+3)-F(2n+1)

而 F(2n+3)=F(2n+2)+F(2n+1),所以:

F(2n+2)=F²(n+2)-F²(n) --公式⑤

还有一个公式:

F(n+1)*F(n-1)-F²(n)=(-1)ⁿ  --公式⑥



目录
相关文章
|
18小时前
|
Python
python幂运算——计算x的y次方
python幂运算——计算x的y次方
27 0
|
17小时前
|
Python
python计算线段角度
python计算线段角度
3 0
|
17小时前
|
安全 数据安全/隐私保护 Python
Python的整型在计算中具有以下优势
【5月更文挑战第6天】Python整型提供任意精度整数计算,无溢出风险;支持多种算术运算,操作简便;作为不可变类型保证数据安全;能进行高级数学运算,并有NumPy等库加持,适合数值分析和科学计算。
19 0
|
17小时前
|
Python
Python的整型在计算中的精度可以通过使用二进制或十进制表示来体现
【5月更文挑战第6天】Python整型支持十、二、八、十六进制表示,其中十进制默认,二进制(0b前缀)、八进制(0o前缀)、十六进制(0x前缀)。计算时以二进制精度处理,确保结果准确。例如:123的二进制是0b1111011,八进制是0o173,十六进制是0x7b。
14 0
|
17小时前
|
Python
Python计算股票投资组合的风险价值(VaR)
Python计算股票投资组合的风险价值(VaR)
|
18小时前
|
数据可视化 Python
【视频】风险价值VaR原理与Python蒙特卡罗Monte Carlo模拟计算投资组合实例
【视频】风险价值VaR原理与Python蒙特卡罗Monte Carlo模拟计算投资组合实例
|
18小时前
|
Python Serverless API
Python风险价值计算投资组合VaR、期望损失ES
Python风险价值计算投资组合VaR、期望损失ES
Python风险价值计算投资组合VaR、期望损失ES
|
18小时前
|
数据可视化 Python
PYTHON贝叶斯推断计算:用BETA先验分布推断概率和可视化案例
PYTHON贝叶斯推断计算:用BETA先验分布推断概率和可视化案例
|
18小时前
|
数据可视化 Python
Python蒙特卡罗(Monte Carlo)模拟计算投资组合的风险价值(VaR)
Python蒙特卡罗(Monte Carlo)模拟计算投资组合的风险价值(VaR)
|
18小时前
|
数据可视化 Serverless API
Python风险价值计算投资组合VaR(Value at Risk )、期望损失ES(Expected Shortfall)
Python风险价值计算投资组合VaR(Value at Risk )、期望损失ES(Expected Shortfall)