C++求解斐波那契数列的若干方法

问题链接:https://www.acwing.com/problem/content/description/207/

问题描述

在这里插入图片描述

方法一:递归

对于斐波那契数来说,每一项都等于他的前两项之和,他的第 0 0 0项和第 1 1 1项分别为 0 , 1 0,1 0,1,那么因为有重复的地方,所有可以使用递归来进行求解

#include <iostream>
using namespace std;

int f_final(int n) {
    if(n < 2) return n;
    return f_final(n-1) + f_final(n-2);
}

int main() {
    int n;
    cin >> n;
    cout << f_final(n) << endl;
    return 0;
}

递归计算的节点个数是 O ( 2 n ) O(2^n) O(2n)的级别的,存在大量重复计算。
时间复杂度是 O ( 2 n ) O(2^n) O(2n),一秒内大约能算到第四十项左右。
在这里插入图片描述

重复计算的原因

每一个递归的程序,都可以绘制一棵树,以表示他的执行过程
在这里插入图片描述
由此可以发现,递归的过程中,因为没有记忆话的能力,所有他做了很多的无用功,大量的重复计算,可以使用记忆化进行优化

方法二:记忆化搜索

可以开辟一个数组用于记录计算过的值,下一次再被搜索到的时候,就可以直接得到结果,而不需要继续递归调用了

#include <iostream>
using namespace std;

const int N = 1e6 + 10, mod = 1e9 + 7;
int f[N];

int f_final(int n) {
    if(f[n]) return f[n];
    if(n < 2) return f[n] = n;
    return f[n] = (f_final(n-1) + f_final(n-2)) % mod;
}

int main() {
    int n;
    cin >> n;
    cout << f_final(n) << endl;
    return 0;
}

总共有 n n n 个状态,计算每个状态的复杂度是 O ( 1 ) O(1) O(1),所以时间复杂度是 O ( n ) O(n) O(n)

一秒内可以算 n = 1 0 7 n=10^7 n=107 ,但由于是递归计算,递归层数太多会爆栈,大约只能算到 n = 1 0 5 n=10^5 n=105 级别

方法三:非递归

#include <iostream>
using namespace std;

const int N = 1e6 + 10, mod = 1e9 + 7;
int f[N];

int f_final(int n) {
    f[1] = 1;
    for(int i = 2; i <= n; i++) 
        f[i] = (f[i-1] + f[i-2]) % mod;
    return f[n];
}

int main() {
    int n;
    cin >> n;
    cout << f_final(n) << endl;
    return 0;
}

总共计算 n n n 个状态,所以时间复杂度是 O ( n ) O(n) O(n)
但是他的瓶颈在于空间复杂度,需要开一个长度是 n n n 的数组,当 n = 1 0 8 n=10^8 n=108 时,需要的内存是 4 × 1 0 8 1024 × 1024 ≈ 381 M B \frac{4\times10^8}{1024\times1024}\approx381MB 1024×10244×108381MB,很容易超内存

方法四:递推 + 滚动变量

从上面递推的过程中,可以发现,对于每一个状态,他只与前面两个状态有关,所有就可以对空间进行一次优化,我们不需要一直报存前面的每一个状态的结果

#include <iostream>
using namespace std;

const int mod = 1e9 + 7;


int f_final(int n) {
    if(n < 2) return n;
    int a = 0, b = 1, c;
    for(int i = 2; i <= n; i++) {
        c = (a + b) % mod;
        a = b;
        b = c;
    }
        
    return c;
}

int main() {
    int n;
    cin >> n;
    cout << f_final(n) << endl;
    return 0;
}

时间复杂度还是 O ( n ) O(n) O(n),空间复杂度变成了 O ( 1 ) O(1) O(1)

方法五:快速幂 + 矩阵乘法

在这里插入图片描述

本题中,他的数据范围非常的大,上面的方法都会超时的,必须加以优化

对于斐波那契数列中,相邻的两项 f n f_n fn f n − 1 f_{n-1} fn1,可以进行如下变化:
{ f n f n − 1 } \left\{ \begin{matrix} f_n\\ f_{n-1}\\ \end{matrix} \right\} {fnfn1}
⇒ { f n − 1 + f n − 2 f n − 1 } \Rightarrow \left\{ \begin{matrix} f_{n-1} + f_{n-2}\\ f_{n-1}\\ \end{matrix} \right\} {fn1+fn2fn1}
⇒ { 1 × f n − 1 + 1 × f n − 2 1 × f n − 1 + 0 × f n − 2 } \Rightarrow \left\{ \begin{matrix} 1 \times f_{n-1} + 1 \times f_{n-2}\\ 1 \times f_{n-1} + 0 \times f_{n-2}\\ \end{matrix} \right\} {1×fn1+1×fn21×fn1+0×fn2}
⇒ { 1 1 1 0 } × { f n − 1 f n − 2 } \Rightarrow \left\{ \begin{matrix} 1 & 1\\ 1 &0 \\ \end{matrix} \right\} \times \left\{ \begin{matrix} f_{n-1} \\ f_{n-2}\\ \end{matrix} \right\} {1110}×{fn1fn2}

所以说,想要计算第 n n n项,可以推导出这样一个公式
{ 1 1 1 0 } n − 1 × { f 1 f 0 } \left\{ \begin{matrix} 1 & 1\\ 1 &0 \\ \end{matrix} \right\}^{n-1} \times \left\{ \begin{matrix} f_{1} \\ f_{0}\\ \end{matrix} \right\} {1110}n1×{f1f0}
⇒ { 1 1 1 0 } n − 1 × { 1 0 } \Rightarrow \left\{ \begin{matrix} 1 & 1\\ 1 &0 \\ \end{matrix} \right\}^{n-1} \times \left\{ \begin{matrix} 1 \\ 0\\ \end{matrix} \right\} {1110}n1×{10}

程序

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

typedef long long LL;
const int MOD = 10000;

// 二维矩阵连乘
void Mul(int a[][2],int b[][2],int c[][2]) {
    int t[][2] = { {0,0}, {0,0} };
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
            for(int k = 0; k < 2; k++)
                t[i][j] = (t[i][j] + a[i][k] * b[k][j]) % MOD;
    
    memcpy(c,t,sizeof t);
}

int f_final(int n) {
    if(n < 2) return n;
    
    int res[][2]    = { {1,0},{0,1} };      // 1
    int t[][2]      = { {1,1},{1,0} };      
    int k = n - 1;
    while(k > 0) {
        if(k & 1) Mul(res,t,res);
        Mul(t,t,t);
        k >>= 1;
    }
    
    int x[] = {1,0};
    int c[] = {0,0};
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
            c[i] = (c[i] + x[j] * res[j][i]) % MOD;
    return c[0];
}

int main() {
    int n;
    while(cin >> n , n != -1) {
        cout << f_final(n) << endl;
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值