斐波那契数的递归算法和动态规划算法

斐波那契数

  • 1,1,2,3,5,8,13,21….从第3个数开始,每个数等于它前面的两个数之和。
  • 斐波那契公式为:
    {F(0)=F(1)=0F(n)=F(n1)+F(n2)  n3 { F ( 0 ) = F ( 1 ) = 0 F ( n ) = F ( n − 1 ) + F ( n − 2 )     n ≥ 3

递归算法

  • 从公式中能看出,可以采用递归的算法算第n个斐波那契数的值。
  • 递归基准条件(终止条件)为 n==0或1。
int Fabonacci(int n)
{
    if (n <= 1)
        return 1;
    else
        return Fabonacci(n - 1) + Fabonacci(n - 2);
}

算法评价

  • 斐波那契数递归算法是递归应用的一个不好的例子。
  • 原因在于,每次计算都要执行两次递归,而且两次递归产生了大量的重复计算。
  • 例如要计算F(4):那么要计算出F(3)和F(2),F(3)通过F(2)和F(1)计算返回,F(3)的F(2)通过F(1)和F(0)计算返回,F(4)的F(2)通过F(1)和F(0)计算返回。整个过程F(0)计算2次、F(1)计算3次、F(2)计算2次、F(3)和F(4)分别计算1次。
  • 大量重复的计算,使得复杂度呈指数爆炸增长。

动态规划的算法

  • 动态规划的算法把原问题分成一个个子问题,记录子问题的解来求原问题的解。
  • 例如:

    F(n)=F(n1)+F(n2)  n3 F ( n ) = F ( n − 1 ) + F ( n − 2 )     n ≥ 3

  • 每次记录子问题F(n-1)和F(n-2),下次就能直接拿来计算F(n)。

  • 动态规划也有基准即子问题的边界,这里和递归一样F(0)=F(1)=0。
int FabonacciD(int n)
{
    int Fa = 1;     // 初始化Fa=1,Fa为要求第n个数值的前两位数即n-2个数值
    int Fb = 1;     // 初始化Fb=1,Fb为要求第n个数值的前一位数即n-1个数值
    int Fab = 0;
    for (int i = 2;i <= n;i++)
    {
        Fab = Fa + Fb;  // Fab 每次由位于它之前的两个斐波那契数相加
        Fa = Fb;        // 更新Fa数值为原来的Fb
        Fb = Fab;       // 更新Fb数值为原来的Fab
    }
    return Fab;
}

算法评价

  • 每个F(n)只需计算一次然后记录下来,避免了大量重复的计算。
  • 动态规划通过牺牲空间复杂度来换取时间复杂度的降低。单大多情况下牺牲是值得的。

附斐波那契数两种计算方式及运行时间比较C++

#include<iostream>
#include<ctime>

using namespace std;

int Fabonacci(int n)
{
    if (n <= 1)
        return 1;
    else
        return Fabonacci(n - 1) + Fabonacci(n - 2);
}

int FabonacciD(int n)
{
    int Fa = 1;     // 初始化Fa=1,Fa为要求第n个数值的前两位数即n-2个数值
    int Fb = 1;     // 初始化Fb=1,Fb为要求第n个数值的前一位数即n-1个数值
    int Fab = 0;
    for (int i = 2;i <= n;i++)
    {
        Fab = Fa + Fb;  // Fab 每次由位于它之前的两个斐波那契数相加
        Fa = Fb;        // 更新Fa数值为原来的Fb
        Fb = Fab;       // 更新Fb数值为原来的Fab
    }
    return Fab;
}

int main()
{
    int n = 40;
    clock_t start, finish;
    cout << "NO." << n << "'s Fabonacci number in a recursion way:\n";
    start = clock();
    cout << Fabonacci(n) << endl;
    finish = clock();
    cout << "Cost Time: ";
    cout << static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000 << "ms" << endl;

    cout << "NO." << n << "'s Fabonacci number in a Dynamic programming way:\n";
    start = clock();
    cout << FabonacciD(n) << endl;
    finish = clock();
    cout << "Cost Time: ";
    cout << static_cast<double>(finish - start) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    system("pause");

    return 0;
}
  • 运行结果
NO.40's Fabonacci number in a recursion way:
165580141
Cost Time: 3980ms
NO.40's Fabonacci number in a Dynamic programming way:
165580141
Cost Time: 0ms
请按任意键继续. . .
  • 从计算第40个斐波那契数可以明显看出,采用动态规划的方式计算速度远远快于采用递归的方式计算。
    原文作者:递归算法
    原文地址: https://blog.csdn.net/weixin_40170902/article/details/80835750
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞