编程笔记(一)求斐波那契数列的第n项的三大方法

本文章示例代码使用python语言。不同语言的朋友也可以看看算法。

我们知道斐波那契数列f(n)有以下性质:
1. f ( 0 ) = 0 , f ( 1 ) = 1 f(0)=0,f(1)=1 f(0)=0,f(1=1
2. f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2)
那么,对于一个指定的数字n,我们应该怎么求f(n)呢?
法一:递归法

def f(n):
    if n==0:
        return 0
    elif n==1:
        return 1
    else:
        ans=f(n-1)+f(n-2)
        return(ans)
n=int(input())
print(f(n))

时间复杂度为O(n^2)
n=30时所用时间:400ms左右
当n过大时建议不使用。
法二:通项公式法
简略版
我们知道斐波那契数列通项:
f(n)= 1 5 ( ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ) \frac{1}{\sqrt 5}((\frac{1+\sqrt 5}{2})^n-(\frac{1-\sqrt 5}{2})^n) 5 1((21+5 )n(215 )n)
(想看证明可评论回复我)
直接代入n:

from math import*
n=int(input())
ans=1/sqrt(5)*((((1+sqrt(5))/2)**n)-(((1-sqrt(5))/2)**n))
print(int(ans))

时间复杂度为O(n),但n过大(n>40)会造成浮点数误差累积,即算出来的f(n)与真实值相差较大。
改进版:
由f(n)= 1 5 ( ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ) \frac{1}{\sqrt 5}((\frac{1+\sqrt 5}{2})^n-(\frac{1-\sqrt 5}{2})^n) 5 1((21+5 )n(215 )n)
f(n)= 1 5 1 2 n ( ( 1 + 5 ) n − ( 1 − 5 ) n ) \frac{1}{\sqrt 5}\frac{1}{2^n}(({1+\sqrt 5})^n-(1-\sqrt 5)^n) 5 12n1((1+5 )n(15 )n)
再二项式展开化简(想看展开过程可评论回复我):
f(n)= 1 2 n − 1 ( C n 1 5 0 + C n 3 5 1 + C n 5 5 2 + . . . . . . + C n k 5 k − 1 2 ) \frac{1}{2^{n-1}}(C_n^15^0+C_n^35^1+C_n^55^2+......+C_n^k5^{\frac{k-1}{2}}) 2n11(Cn150+Cn351+Cn552+......+Cnk52k1)

n n n为奇数时, k = n − 1 k=n-1 k=n1,当n为偶数时, k = n k=n k=n.

#定义阶乘
def factorial(n):
    s=1
    if n<0:
        print('error')
    else:
        for i in range(1,n+1):
            s=s*i
        return(s)
#定义组合数
def combination(n,k):
    if n<k or n<=0 or k<0:
        print('error')
    elif k==0:
        return(1)
    else:
        X=factorial(n)
        Y=factorial(n-k)*factorial(k)
        return(X//Y)    
n=int(input())
ans,k=0,1
while k<=n:
    ans=ans+combination(n,k)*(5**((k+1)//2-1))
    k=k+2
ans=int(ans//(2**(n-1)))
print(ans)

避免了公式中 5 \sqrt{5} 5 的浮点数误差。
时间复杂度为 O(n),
当n=1000时,运行速度:400ms左右。
法三 矩阵快速幂
矩阵与斐波那契数列:
因为 [ f ( 1 ) f ( 0 ) ] \begin{bmatrix} f(1)&f(0)\end{bmatrix} [f(1)f(0)] [ 1 1 1 0 ] \begin{bmatrix} 1&1\\1&0\end{bmatrix} [1110]= [ f ( 1 ) + f ( 0 ) f ( 1 ) ] \begin{bmatrix} f(1)+f(0)&f(1)\end{bmatrix} [f(1)+f(0)f(1)]= [ f ( 2 ) f ( 1 ) ] \begin{bmatrix} f(2)&f(1)\end{bmatrix} [f(2)f(1)]
因此有结论
[ f ( n ) f ( n − 1 ) ] \begin{bmatrix} f(n)&f(n-1)\end{bmatrix} [f(n)f(n1)]= [ f ( 1 ) f ( 0 ) ] \begin{bmatrix} f(1)&f(0)\end{bmatrix} [f(1)f(0)] [ 1 1 1 0 ] n − 1 \begin{bmatrix} 1&1\\1&0\end{bmatrix}^{n-1} [1110]n1
快速幂:
当n为奇数时,
A n = A ( A 2 ) n − 1 2 A^n=A(A^2)^{\frac{n-1}{2}} An=A(A2)2n1
当n为偶数时:
A n = ( A 2 ) n 2 A^n=(A^2)^{\frac{n}{2}} An=(A2)2n
利用矩阵快速幂,可以快速求解。

#矩阵乘法
def matrixmultiply(a,b):
    ans,C=[],[]
    if len(a[0])==len(b):
        for i in range(1,len(a)+1):
            C=[]
            for j in range(1,len(b[0])+1):
                c=0
                for k in range(1,len(b)+1):
                    c+=a[i-1][k-1]*b[k-1][j-1]
                C.append(c)
            ans.append(C)
        return(ans)
    else:
        return('error')
#求单位矩阵
def matrixelement(a):
    ans=[]
    if len(a[0])==len(a):
        for i in range(1,len(a)+1):
            C=[]
            for j in range(1,len(a[0])+1):
                c=0
                if i==j:
                    c=1
                else:
                    c=0
                C.append(c)
            ans.append(C)
        return(ans)
    else:
        return('error')        
#求矩阵幂
def matrixqiuckpow(a,b):
    ans=matrixelement(a)
    while b!=0:
        if b%2==1:
            ans=matrixmultiply(ans,a)
            b=b-1
        else:
            a=matrixmultiply(a,a)
            b=b//2
    return(ans)
n=int(input())
a=[[1,1],[1,0]]
f1=[[1,0]]
ans=matrixmultiply(f1,matrixqiuckpow(a,n))
print(ans[0][1])

矩阵快速幂的时间复杂度是O(log(n))
当n=100,000时,运行时间:700ms左右。
(大部分时间是用在print()上了,真正运算时间可能只有几毫秒,读者可以试试n=10,000时输出数字有多大)
总结:设计程序时应该选择时间复杂度尽可能小的算法,O(log(n))是比较快速的算法之一。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值