本文章示例代码使用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(n−1)+f(n−2)
那么,对于一个指定的数字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)
51((21+5)n−(21−5)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)
51((21+5)n−(21−5)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)
512n1((1+5)n−(1−5)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}})
2n−11(Cn150+Cn351+Cn552+......+Cnk52k−1)
当 n n n为奇数时, k = n − 1 k=n-1 k=n−1,当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(n−1)]=
[
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]n−1
快速幂:
当n为奇数时,
A
n
=
A
(
A
2
)
n
−
1
2
A^n=A(A^2)^{\frac{n-1}{2}}
An=A(A2)2n−1
当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))是比较快速的算法之一。