F i b o n a c c i Fibonacci Fibonacci 数列: F 0 = 0 , F 1 = 1 , F n = F n − 1 + F n − 2 ( n ≥ 2 ) F_0=0,F_1=1,F_n=F_{n-1}+F_{n-2}(n\ge 2) F0=0,F1=1,Fn=Fn−1+Fn−2(n≥2) .
本文重点关注 F i b o n a c c i Fibonacci Fibonacci 数列的通项公式与第 n n n 项的计算.
一、通项公式
构造矩阵递推式:
{
F
n
+
1
=
F
n
+
F
n
−
1
,
F
n
=
F
n
+
0
⋅
F
n
−
1
.
\begin{cases} F_{n+1}=F_n+F_{n-1},\\ F_{n}=F_n+0\cdot F_{n-1}. \end{cases}
{Fn+1=Fn+Fn−1,Fn=Fn+0⋅Fn−1.
⇒ ( F n + 1 F n ) = ( 1 1 1 0 ) ( F n F n − 1 ) = ( 1 1 1 0 ) 2 ( F n − 1 F n − 2 ) = ⋯ = ( 1 1 1 0 ) n ( F 1 F 0 ) . \begin{aligned} \Rightarrow \begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix} &=\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}\begin{pmatrix} F_{n} \\ F_{n-1} \end{pmatrix}\\ &=\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^{2}\begin{pmatrix} F_{n-1} \\ F_{n-2} \end{pmatrix}\\ &=\cdots \\ &=\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^{n}\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix}. \end{aligned} ⇒(Fn+1Fn)=(1110)(FnFn−1)=(1110)2(Fn−1Fn−2)=⋯=(1110)n(F1F0).
令 A = ( 1 1 1 0 ) A=\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} A=(1110) , A A A 为实对称矩阵,因此必定存在可逆矩阵 P P P ,使 P − 1 A P = Λ P^{-1}AP=\Lambda P−1AP=Λ .
取 P = ( − 2 2 5 + 1 5 − 1 ) P=\begin{pmatrix} -2 & 2 \\ \sqrt{5}+1 & \sqrt{5}-1 \end{pmatrix} P=(−25+125−1) ,则
P − 1 = ( 1 10 + 2 5 1 10 − 2 5 ) ( − 2 5 + 1 2 5 − 1 ) P^{-1}=\begin{pmatrix} \frac{1}{10+2\sqrt{5}} & \\ & \frac{1}{10-2\sqrt{5}} \end{pmatrix}\begin{pmatrix} -2 & \sqrt{5}+1 \\ 2 & \sqrt{5}-1 \end{pmatrix} P−1=(10+25110−251)(−225+15−1) ,
满足 P − 1 A P = Λ = ( 1 − 5 2 1 + 5 2 ) P^{-1}AP=\Lambda=\begin{pmatrix} \dfrac{1-\sqrt{5}}{2} & \\ & \dfrac{1+\sqrt{5}}{2} \end{pmatrix} P−1AP=Λ=⎝⎜⎛21−521+5⎠⎟⎞ .
于是
⇒
(
F
n
+
1
F
n
)
=
A
n
(
F
1
F
0
)
=
P
Λ
n
P
−
1
(
1
0
)
=
(
−
1
5
(
1
−
5
2
)
n
+
1
+
1
5
(
1
+
5
2
)
n
+
1
−
1
5
(
1
−
5
2
)
n
+
1
5
(
1
+
5
2
)
n
)
.
\begin{aligned} \Rightarrow \begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix} &=A^{n}\begin{pmatrix} F_{1} \\ F_{0} \end{pmatrix}\\ &=P\Lambda ^{n}P^{-1}\begin{pmatrix} 1 \\ 0 \end{pmatrix}\\ &=\begin{pmatrix} \dfrac{-1}{\sqrt{5}}\left( \dfrac{1-\sqrt{5}}{2}\right) ^{n+1} +\dfrac{1}{\sqrt{5}}\left( \dfrac{1+\sqrt{5}}{2}\right) ^{n+1} \\ \dfrac{-1}{\sqrt{5}}\left( \dfrac{1-\sqrt{5}}{2}\right) ^{n} +\dfrac{1}{\sqrt{5}}\left( \dfrac{1+\sqrt{5}}{2}\right) ^{n} \end{pmatrix}. \end{aligned}
⇒(Fn+1Fn)=An(F1F0)=PΛnP−1(10)=⎝⎜⎜⎜⎜⎛5−1(21−5)n+1+51(21+5)n+15−1(21−5)n+51(21+5)n⎠⎟⎟⎟⎟⎞.
故
F
i
b
o
n
a
c
c
i
Fibonacci
Fibonacci 数列的通项公式为
F
n
=
1
5
(
1
+
5
2
)
n
−
1
5
(
1
−
5
2
)
n
.
(
n
≥
0
)
F_n=\dfrac{1}{\sqrt{5}}\left( \dfrac{1+\sqrt{5}}{2}\right) ^{n}-\dfrac{1}{\sqrt{5}}\left( \dfrac{1-\sqrt{5}}{2}\right) ^{n}.(n\ge 0)
Fn=51(21+5)n−51(21−5)n.(n≥0)
由于
∣
F
n
−
1
5
(
1
+
5
2
)
n
∣
=
1
5
(
5
−
1
2
)
n
≤
1
5
<
1
2
.
(
n
≥
0
)
\left| F_{n}-\dfrac{1}{\sqrt{5}}\left( \dfrac{1+\sqrt{5}}{2}\right) ^{n}\right| =\dfrac{1}{\sqrt{5}}\left( \dfrac{\sqrt{5}-1}{2}\right) ^{n}\le \dfrac{1}{\sqrt{5}} \lt\dfrac 12.(n\ge 0)
∣∣∣∣∣Fn−51(21+5)n∣∣∣∣∣=51(25−1)n≤51<21.(n≥0)
因此
F
n
=
⌊
1
5
(
5
+
1
2
)
n
+
1
2
⌋
.
(
n
≥
0
)
F_{n}=\left\lfloor \dfrac{1}{\sqrt{5}}\left( \dfrac{\sqrt{5}+1}{2}\right) ^{n}+\dfrac{1}{2}\right\rfloor.(n\ge 0)
Fn=⌊51(25+1)n+21⌋.(n≥0)
二、计算
方法一:利用递推公式
{ F n = F n − 1 + F n − 2 , n ≥ 2 , F 0 = 0 , F 1 = 1. \begin{cases}F_{n}=F_{n-1}+F_{n-2},n\geq 2,\\ F_{0}=0,F_{1}=1.\end{cases} {Fn=Fn−1+Fn−2,n≥2,F0=0,F1=1.
时间复杂度 O ( n ) O(n) O(n) .
def get_fib1(n: int):
"""递推式求Fibonacci第n项"""
if n < 0:
return None
if n == 0 or n == 1:
return n
a, b = 0, 1
for i in range(2, n + 1):
a += b
a, b = b, a
return b
方法二:矩阵快速幂
令 A = ( 1 1 1 0 ) A=\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} A=(1110) ,则 ( F n + 1 F n ) = A n ( 1 0 ) \begin{pmatrix} F_{n+1} \\ F_{n} \end{pmatrix}=A^{n}\begin{pmatrix} 1 \\ 0 \end{pmatrix} (Fn+1Fn)=An(10) .
显然 F n = A 2 , 1 n F_n=A^n_{2,1} Fn=A2,1n .
时间复杂度 O ( log 2 n ) O(\log_2n) O(log2n) .
F 47 F_{47} F47 超过 4 4 4 字节整数范围导致结果溢出.
def get_fib2(n: int):
"""矩阵快速幂计算F_n
n=47时溢出
"""
if n < 0:
return None
import numpy as np
tmp = [[1, 0], [0, 1]]
a = [[1, 1], [1, 0]]
while n > 0:
if n & 1:
tmp = np.dot(tmp, a)
a = np.dot(a, a)
n >>= 1
return tmp[1][0]
方法三:通项公式计算
通项公式:
F
n
=
⌊
1
5
(
5
+
1
2
)
n
+
1
2
⌋
.
F_{n}=\left\lfloor \dfrac{1}{\sqrt{5}}\left( \dfrac{\sqrt{5}+1}{2}\right) ^{n}+\dfrac{1}{2}\right\rfloor.
Fn=⌊51(25+1)n+21⌋.
时间复杂度取决于计算 ((1 + sqrt5) / 2) ** n
的速度.
n = 71 n=71 n=71 时会由于浮点误差过大导致结果不对.
def get_fib3(n: int):
"""利用通项公式求F_n
n=71时浮点误差过大
"""
if n < 0:
return None
import math
sqrt5 = math.sqrt(5)
return int(1 / sqrt5 * ((1 + sqrt5) / 2) ** n + 0.5)
使用高精度可以在一定程度上解决浮点误差问题.
若运算精度取 28 28 28 , n = 123 n=123 n=123 时结果错误.
若运算精度取 100 100 100 , n = 464 n=464 n=464 时结果错误.
若运算精度取 1000 1000 1000 , n = 4768 n=4768 n=4768 时结果错误.
def get_fib4(n: int):
"""利用通项公式求F_n"""
import decimal
from decimal import Decimal
decimal.getcontext().prec = 1000 # 运算精度,默认值 28
sqrt5 = Decimal(5).sqrt()
_1 = Decimal(1)
_2 = Decimal(2)
return int(_1 / sqrt5 * ((_1 + sqrt5) / _2) ** n + _1 / _2)