fibonacci数列

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ΛS1,可得:
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ΛkS1Sc=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/2Ak/2
如果是奇次幂,就再乘以 A A A
到此为止,时间复杂度降为 O ( log ⁡ n ) O(\log n) O(logn).


参考方案

  1. 迭代求解
% 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
  1. 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
  1. 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
  • 可以计算误差是多少

  1. 当时看到这的时候,有条弹幕说一辈子也想不到这个方法 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值