斐波那契数列

斐波那契数列

定义: F 0 = 0 , F 1 = 1 , F n = F n − 1 + F n − 2 F_0 = 0,\quad F_1 = 1, \quad F_n = F_{n-1} + F_{n-2} F0=0,F1=1,Fn=Fn1+Fn2

F n F_n Fn所组成的数列叫做斐波那契数列。

更为详细的定义见百度百科:斐波那契数列

该数列的前几项如下:
0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , . . . 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... 0,1,1,2,3,5,8,13,21,34,55,89,...
很显然,第 n n n个斐波那契数可以用这个递推公式以线性时间复杂度 O ( n ) O(n) O(n)递推求解出来。

C++代码:

#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e6 + 5;
int f[N];
int main()
{
    f[0] = 0; f[1] = 1;
    int n;
    cin >> n;
	for(int i = 2; i <= n; i ++ ) {
    	f[i] = (f[i-1] + f[i-2]) % mod; //要注意取模,防止数字过大溢出
	}
    cout << f[n] << endl;
    return 0;
}

这种方法很简单,也很容易理解。但我们不难发现,当 n n n 比较大的时候,求出 F n F_n Fn 的时间代价就比较大了。并且也会占用极大地内存,因为数列中的每一个数都需要占4个字节。那么有没有更好的方法去求斐波那契数列呢?

矩阵形式

\quad 斐波那契数列的递推可以用矩阵乘法的形式表达:
[ F n − 1 F n ] = [ F n − 2 F n − 1 ] ⋅ [ 0 1 1 1 ] \begin{bmatrix} F_{n-1} &F_n \end{bmatrix} =\begin{bmatrix} F_{n-2} &F_{n-1} \end{bmatrix} \cdot\begin{bmatrix} 0 &1 \\ 1 &1 \\ \end{bmatrix} [Fn1Fn]=[Fn2Fn1][0111]
\quad P = [ 0 1 1 1 ] P = \begin{bmatrix}0 &1\\1 &1 \end{bmatrix} P=[0111],我们得到
[ F n F n + 1 ] = [ F 0 F 1 ] ⋅ P n \begin{bmatrix}F_n &F_{n+1}\end{bmatrix}=\begin{bmatrix}F_0 &F_1\end{bmatrix}\cdot P^{n} [FnFn+1]=[F0F1]Pn
\quad 于是我们可以用矩阵乘法在 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn)的时间内计算斐波那契数列。

快速倍增法

\quad 使用上面的方法我们可以得到以下等式:
F 2 k = F k ( 2 F k + 1 − F k ) F 2 k + 1 = F k + 1 2 + F k 2 \begin{aligned} F_{2k}=F_{k}(2F_{k+1}-F_{k}) \\ F_{2k+1}=F_{k+1}^{2}+F_{k}^{2} \end{aligned} F2k=Fk(2Fk+1Fk)F2k+1=Fk+12+Fk2
\quad 于是可以通过这样的方法快速计算两个相邻的斐波那契数(常数比矩乘小)。代码如下,返回值是一个二元组 ( F n , F n + 1 ) (F_n, F_{n+1}) (Fn,Fn+1)

C++代码实现如下:

pair<int, int> fib(int n)
{
    if(n == 0) {
        return {0, 1};
    }
    pair<int, int> p = fib(n >> 1);
    int c = p.first * (2 * p.second - p.first);
    int d = p.first * p.first + p.second * p.second;
    if (n & 1)
        return {d, c + d};
    else
        return {c, d};
}

矩阵法

  • 对矩阵形式的简单证明:

设转换矩阵为 P P P ,则有:
[ F n − 2 F n − 1 ] ⋅ P = [ F n − 1 F n ] \begin{bmatrix} F_{n-2} &F_{n-1} \end{bmatrix}\cdot P=\begin{bmatrix} F_{n-1} &F_{n} \end{bmatrix} [Fn2Fn1]P=[Fn1Fn]
由矩阵相乘的性质可知 P P P 2 × 2 2\times 2 2×2 的矩阵,设 P = ( a b c d ) P=\begin{pmatrix} a &b \\ c &d \end{pmatrix} P=(acbd)

那么 [ F n − 2 F n − 1 ] ⋅ ( a b c d ) = [ F n − 1 F n − 1 + F n − 2 ] \begin{bmatrix} F_{n-2} &F_{n-1} \end{bmatrix}\cdot\begin{pmatrix}a &b \\ c &d \end{pmatrix}=\begin{bmatrix} F_{n-1} &F_{n-1}+F_{n-2} \end{bmatrix} [Fn2Fn1](acbd)=[Fn1Fn1+Fn2]

则: { a ⋅ F n − 2 + c ⋅ F n − 1 = F n − 1 b ⋅ F n − 2 + d ⋅ F n − 1 = F n − 1 + F n − 2 \begin{cases}a\cdot F_{n-2}+c\cdot F_{n-1}=F_{n-1}\\b\cdot F_{n-2}+d\cdot F_{n-1}=F_{n-1}+F_{n-2} \end{cases} {aFn2+cFn1=Fn1bFn2+dFn1=Fn1+Fn2

解得: { a = 0 b = 1 c = 1 d = 1 \begin{cases}a=0\\b=1\\c=1\\d=1 \end{cases} a=0b=1c=1d=1

∴ P = ( 0 1 1 1 ) \therefore P=\begin{pmatrix}0 &1\\1 &1 \end{pmatrix} P=(0111),由此得证。

那么我们可以用矩阵快速幂快速求出 F n 、 F n + 1 F_{n}、F_{n+1} FnFn+1 的值。

快速幂的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn) ,所以矩阵法求斐波那契数列时间复杂度是 Θ ( log ⁡ n ) \Theta(\log n) Θ(logn)

C++代码实现如下:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int mod = 1e9 + 7;
struct matrix {
    ll cell[2][2];
    matrix(ll a = 0, ll b = 0, ll c = 0, ll d = 0) {
		cell[0][0] = a;
        cell[0][1] = b;
        cell[1][0] = c;
        cell[1][1] = d;
    }
}one(0, 1, 1, 1);
matrix mul(matrix a, matrix b) {
	matrix r;
    for(int i = 0; i < 2; i ++)
        for(int j = 0; j < 2; j ++)
            for(int k = 0; k < 2; k ++) {
                r.cell[i][j] += 1LL * a.cell[i][k] * b.cell[k][j] % mod; //防止整型溢出
                r.cell[i][j] = r.cell[i][j] % mod;
            }
    return r;
}
matrix qpow(ll k) {
    matrix r(0, 1, 0, 0);
    matrix cm(0, 1, 1, 1);
    while(k) {
		if(k & 1) r = mul(r, cm);
        cm = mul(cm, cm);
        k >>= 1;
    }
    return r;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    ll n;
    cin >> n;
    matrix t = qpow(n);
    cout << t.cell[0][0] << endl;
    return 0;
}

本文章全部内容均为原创,如需转载请注明出处

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡沛玮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值