关于斐波那契数列三种解法及时间复杂度分析

斐波那契数列: f(n)=f(n-1)+f(n-2); n>=2
f(0)=0; f(1)=1;
即有名的兔子繁衍问题。
斐波那契数列共有三种解法,因而写这篇文章总结一下。
1. 递归求解
递归求解比较简单,是大家常见的一种解法。

int fibonacci(int n)
{
    cout<<"calculating "<<n<<endl;
    if (n<=0) {
        return  0;
    }
    if (n==1) {
        return 1;
    }
    return fb(n-1)+fb(n-2);
}

关于这种解法,不再赘述,下面主要说下时间复杂度分析。
设f(n)为参数为n时的时间复杂度,很明显:f(n)=f(n-1)+f(n-2)
这就转化为了数学上的二阶常系数差分方程,并且为其次方程。
即转化为了求f(n)的值,f(n)=f(n-1)+f(n-2)且f(0)=0; f(1)=1;
特征方程为:x^2-x-1=0
得 x=(1±√5)/2
因而f(n)的通解为:
这里写图片描述
由f(0)=0; f(1)=1可解得c_1,c_2
最终可得,时间复杂度为:
这里写图片描述

  1. 第一种解法比较简单,但是多个元素重复计算,因而时间复杂度较高,为了避免重复计算,可进行循环计算减少时间复杂度
    int Fibonacci(int n) {
        if (n<=0) {
            return 0;
        }
        if (n==1) {
            return 1;
        }
        int min=0;
        int max=1;
        int i=2;
        int result=0;
        while (i<=n) {
            result=min+max;
            min=max;
            max=result;
            ++i;
        }
        return result;
    }

第二种算法时间复杂度为O(n)
3. 还有一种时间复杂度更低的算法。
根据上面的递归公式,我们可以得到
这里写图片描述
因而计算f(n)就简化为了计算矩阵的(n-2)次方,而计算矩阵的(n-2)次方,我们又可以进行分解,即计算矩阵(n-2)/2次方的平方,逐步分解下去,由于折半计算矩阵次方,因而时间复杂度为O(log n)
具体代码实现如下:

//
//  main.cpp
//  fibonaccimatrix
//
//  Created by shunagao on 15/8/31.
//  Copyright © 2015年 shunagao. All rights reserved.
//

#include <iostream>
using namespace std;

class Matrix
{
public:
    int n;
    int **m;
    Matrix(int num)
    {
        m=new int*[num];
        for (int i=0; i<num; i++) {
            m[i]=new int[num];
        }
        n=num;
        clear();
    }
    void clear()
    {
        for (int i=0; i<n; ++i) {
            for (int j=0; j<n; ++j) {
                m[i][j]=0;
            }
        }
    }
    void unit()
    {
        clear();
        for (int i=0; i<n; ++i) {
            m[i][i]=1;
        }
    }
    Matrix operator=(const Matrix mtx)
    {
        Matrix(mtx.n);
        for (int i=0; i<mtx.n; ++i) {
            for (int j=0; j<mtx.n; ++j) {
                m[i][j]=mtx.m[i][j];
            }
        }
        return *this;
    }
    Matrix operator*(const Matrix &mtx)
    {
        Matrix result(mtx.n);
        result.clear();
        for (int i=0; i<mtx.n; ++i) {
            for (int j=0; j<mtx.n; ++j) {
                for (int k=0; k<mtx.n; ++k) {
                    result.m[i][j]+=m[i][k]*mtx.m[k][j];
                }   
            }
        }
        return result;
    }
};
int main(int argc, const char * argv[]) {
    unsigned int num=2;
    Matrix first(num);
    first.m[0][0]=1;
    first.m[0][1]=1;
    first.m[1][0]=1;
    first.m[1][1]=0;
    int t;
    cin>>t;
    Matrix result(num);
    result.unit();
    int n=t-2;
    while (n) {
        if (n%2) {
            result=result*first;
            }
        first=first*first;
        n=n/2;
    }
    cout<<(result.m[0][0]+result.m[0][1])<<endl;
    return 0;
}
<think>嗯,用户之前问了走楼梯问题,现在又想知道递归解法时间复杂度怎么计算。看来他们对这个问题有了初步了解,现在想深入理解时间复杂度的部分。首先,我需要回忆一下递归解法时间复杂度计算方法。 用户之前可能已经知道递归解法的基本思路,即f(n) = f(n-1) + f(n-2),但是时间复杂度O(2^n)是怎么来的呢?他们可能对如何推导这个结果感到困惑。我应该从递归树的角度来解释,因为每次调用都会分裂成两个子问题,这样层数增加,节点数指数级增长。 另外,用户可能没有意识到重复计算的问题,或者不明白为什么时间复杂度这么高。可能需要用具体的例子,比如n=5时的递归树,展示有多少重复计算,这样更直观。 还有,用户可能想知道是否有更准确的计算方法,比如使用递推公式来时间复杂度。这时候需要引入主定理或者递推方程,但主定理可能不适用,因为这里递推式是T(n) = T(n-1) + T(n-2) + O(1),这其实是斐波那契的形式,所以时间复杂度其实和斐波那契数列的增长一致,也就是指数级的。 同时,用户可能对时间复杂度的表示方式有疑问,比如为什么是O(2^n)而不是精确的斐波那契数列的闭合公式。需要解释大O表示法的含义,以及如何近似估算。 可能还需要比较递归解法和动态规划的区别,指出动态规划通过存储中间结果避免了重复计算,从而降低了时间复杂度。这样用户能明白为什么递归虽然直观但效率低下,而动态规划更优。 最后,用户可能希望知道有没有优化递归的方法,比如记忆化递归,这样可以保持递归结构但提升效率。这时候可以简要提到记忆化的概念,并指出其时间复杂度会降低到O(n),和动态规划一样。 总结来说,需要分步骤解释递归树的结构,重复计算的问题,推导时间复杂度的过程,以及可能的优化方法,确保用户能够逐步理解。</think>以下是关于递归解法时间复杂度的详细推导过程(以爬楼梯问题为例): ### 基本递归公式 $$T(n) = T(n-1) + T(n-2) + O(1)$$ ### 计算步骤解析 #### 1. 递归树可视化(以n=5为例) ``` f(5) / \ f(4) f(3) / \ / \ f(3) f(2) f(2) f(1) / \ f(2) f(1) ``` (继续展开到叶子节点均为f(1)和f(0)) #### 2. 节点数量分析 每个节点分裂为2个子节点,形成近似满二叉树结构: - 树的高度 ≈ n - 总节点数 ≈ $2^n$ #### 3. 精确数学推导 递推关系式: $$T(n) = T(n-1) + T(n-2) + c$$ (c为常数操作时间) 令 $T(n) + c = S(n)$,则递推式简化为: $$S(n) = S(n-1) + S(n-2)$$ 这正是斐波那契数列的递推关系,因此: $$T(n) = O(F(n)) = O\left( \frac{\phi^n}{\sqrt{5}} \right)$$ 其中 $\phi = \frac{1+\sqrt{5}}{2} \approx 1.618$(黄金分割比) #### 4. 渐进分析 由于 $\phi^n$ 的增长速度与 $2^n$ 同属指数级复杂度,可得: $$T(n) = O(2^n)$$ ### 关键结论 1. **指数级复杂度**:实际计算复杂度为 $O(\phi^n)$,约等于 $O(1.618^n)$ 2. **重复计算**:递归树中存在大量重复节点(如f(3)被计算2次) 3. **优化方向**:通过记忆化存储中间结果,可将复杂度降至 $O(n)$ ### 复杂度对比表 | 方法 | 时间复杂度 | 空间复杂度 | |-----------|--------|--------| | 纯递归 | $O(2^n)$ | $O(n)$ | | 记忆化递归 | $O(n)$ | $O(n)$ | | 动态规划 | $O(n)$ | $O(n)$ | | 优化版动态规划 | $O(n)$ | $O(1)$ | ### 记忆化递归示例 ```python def climb_stairs(n, memo={}): if n <= 1: return 1 if n not in memo: memo[n] = climb_stairs(n-1, memo) + climb_stairs(n-2, memo) return memo[n] ``` 通过添加备忘录,将指数复杂度优化为线性复杂度,这是递归解法的常用优化手段。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值