fibonacci数列是什么
0, 1, 1, 2, 3, 5, 8, 13, 21…
通项公式为
F
n
+
2
=
F
n
+
1
+
F
n
F_{n+2} = F_{n+1} + F_n
Fn+2=Fn+1+Fn
常规解法
- 迭代求解,复杂度为 O ( n ) O(n) O(n)
- 常见于各种语言演示函数递归的递归求解,复杂度为指数级别
前置知识(矩阵解法)
如果一个系统满足方程
u
k
+
1
=
A
u
k
u_{k+1} = Au_k
uk+1=Auk
如何求解
u
k
u_k
uk ? 由:
u
1
=
A
u
0
,
u
2
=
A
2
u
1
,
.
.
.
u_1 = Au_0, u_2 = A^2u_1, ...
u1=Au0,u2=A2u1,...
可知
u
k
=
A
k
u
0
u_k=A^ku_0
uk=Aku0。
如果
A
A
A的特征向量彼此独立,那么
u
0
u_0
u0就可以分解成特征向量的线性组合,即
u
0
=
S
c
u_0=Sc
u0=Sc
S
S
S是由特征向量构成的矩阵,
c
c
c是对应系数。
那么由于
A
=
S
Λ
S
−
1
A = S\Lambda S^{-1}
A=SΛS−1,可得:
u
k
=
A
k
u
0
=
S
Λ
k
S
−
1
⋅
S
c
=
S
Λ
k
c
u_k = A^ku_0 = S\Lambda^{k}S^{-1}\cdot Sc = S\Lambda^kc
uk=Aku0=SΛkS−1⋅Sc=SΛkc
Λ
\Lambda
Λ是有特征值构成的对角矩阵。
也就是说我们将求矩阵的幂转化为求特征值的幂。
矩阵乘法
如何将问题化为上述 u k + 1 = A u k u_{k+1}=Au_k uk+1=Auk的形式
我们有通项公式
F
n
+
2
=
F
n
+
1
+
F
n
F_{n+2} = F_{n+1}+F_n
Fn+2=Fn+1+Fn
利用一个trick,添加等式
F
n
+
1
=
F
n
+
1
F_{n+1} = F_{n+1}
Fn+1=Fn+11
也就是说我们得到这么一组方程
F
n
+
2
=
F
n
+
1
+
F
n
F
n
+
1
=
F
n
+
1
\begin{aligned} F_{n+2} &= F_{n+1}+F_n \\ F_{n+1} &= F_{n+1} \end{aligned}
Fn+2Fn+1=Fn+1+Fn=Fn+1
令
u
k
=
[
F
k
+
1
F
k
]
u_k = \begin{bmatrix} F_{k+1} \\ F_k \end{bmatrix}
uk=[Fk+1Fk]
通过观察可以得到,
A
=
[
1
1
1
0
]
A = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}
A=[1110]
接下来就可以通过上面提到的方法,求
A
A
A的特征值,特征向量,然后求得
u
k
u_k
uk,进而得到
F
k
F_{k}
Fk
存在问题
A A A的特征值存在小数,意味着如果我们通过计算机求解这个问题,那么就会存在误差。
解决方法
我们只能退而求其次,求
A
k
A^k
Ak而不是求
Λ
k
\Lambda^k
Λk(小数,导致出现舍入误差)
但是在求
A
k
A^k
Ak时,如果使用迭代求解,那么时间复杂度仍然为
O
(
n
)
O(n)
O(n),那么我们写到现在的所有东西都将毫无意义,好在我们还可以divide and conquer
求解
A
k
A^k
Ak我们只需要求解
A
k
/
2
A^{k/2}
Ak/2,那么,
A
k
=
A
k
/
2
∗
A
k
/
2
A^k = A^{k/2} * A^{k/2}
Ak=Ak/2∗Ak/2
如果是奇次幂,就再乘以
A
A
A。
到此为止,时间复杂度降为
O
(
log
n
)
O(\log n)
O(logn).
参考方案
- 迭代求解
% iteration
function Fn = fibonacci2(n)
former = 0;
this = 1;
if n < 2
Fn = n;
else
for i = 2:n
temp = this;
this = this + former;
former = temp;
end
Fn = this;
end
- 求 A A A的幂次方
- 需要两个函数,一个求解 A A A的幂次,一个求解 F n F_n Fn
% powerOfA
function An = powerOfA(n)
% 一次幂,返回A
if n == 1
An = [1 1;1 0];
else
temp = powerOfA(floor(n/2));
if mod(n,2) == 0
An = temp * temp;
else
An = temp * temp * [1 1;1 0];
end
end
% fibonacci数列
function Fn = fibonacci(n)
% 只需要算u(k-1)项就可以了
if n < 2
Fn = n;
else
An = powerOfA(n-1);
Fn = An(1);
end
end
- eigenvalue和eigenvector
% eigenvalues and eigenvectors
function Fn = fibonacci3(n)
if n < 2
Fn = n;
else
A = [1 1; 1 0];
[V,D] = eig(A);
u0 = [1;0];
uk_1 = V * D.^(n-1) * (V \ u0);
Fn = uk_1(1);
end
end
- 可以计算误差是多少
当时看到这的时候,有条弹幕说一辈子也想不到这个方法 ↩︎