求解斐波那契数列复杂度分析

前言:斐波那契作为一个算法基础知识,大家一定要掌握,祝大家学得开心~

什么是斐波那契数列(Fibonacci sequence)?

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)。

方式一:递归

F(n)=F(n-1)+F(n-2),利用递归思想,每次计算当前的值时候,就要引用之前的两个值,一步一步的递归,一直到最起始处,直至F(1)和F(2)。

递归算法程序:

long long Fib1(long long n)
{
    assert(n >= 0);
    //递归
    return n < 2 ? n : (Fib1(n - 1) + Fib1(n - 2));
}
View Code

递归算法中有大量的重复计算,效率低下,当需要计算的数稍大一点,就需要很长的计算时间,甚至可能导致栈溢出,但优点是代码简单,容易理解。

递归的深度*每次递归所需的辅助空间的个数 ,所以空间复杂度是:O(N)。

递归算法时间复杂度为:O(2^(N/2)) <= T(N) <= O(2^N)。计算过程有些复杂,需要利用线性代数知识(红衣教主出没~)。

明显,设T(n)为参数为n时的时间复杂度,则T(n)=T(n-1)+T(n-2) 。

这是数学上的二阶常系数差分方程,并且为齐次方程。 特征方程为:x^2-x-1=0 。解得 x=(1±√5)/2 。

 

方式二:非递归

1.创建一个数组,每次将前两个数相加后直接赋给后一个数。这样的话,有N个数就创建一个包含N个数的一维数组,所以空间复杂度为O(N);由于只需从头向尾遍历一边,时间复杂度为O(N)。在多次查询斐波那契数可以先初始化就求出数组,这样查询时间复杂度就是O(1)了。

long long* Fib2(long long n)
{
    assert(n >= 0);
    //非递归
    long long* F = new long long[n + 1];
    F[0] = 0;
    F[1] = 1;
    for (int i = 2; i <= n; i++)
        F[i] = F[i - 1] + F[i - 2];
    
    return F;
}
View Code

2.借助两个变量first和second,每次将first和second相加后赋给third,再将second赋给first,third赋给second,时间复杂度为O(N),空间复杂度为O(1)。只用于求单个斐波那契数,题目中一般不这么写,查询复杂度为O(N)。

long long Fib3(long long n)
{
    assert(n >= 0);
        
    long long first = 0;
    long long second = 1;
        if(n < 2) return n;
        
    long long third;
    for(int i = 2; i <= n; i++)
    {
        third = first + second;
        first = second;
        second = third;
    }
    return third;
}
View Code

计算过程:一般情况下,对步进循环语句只需考虑循环体中语句的执行次数,忽略该语句中步长加1、终值判别、控制转移等成分,当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n)决定的。这里T(n) = 3 * (n - 2) = O(N)。

方式三:快速矩阵幂

先了解一下,什么是矩阵快速幂

斐波那契数列的计算机算法是利用矩阵,二阶三阶都可以,这里举例二阶。

我们利用快速矩阵幂算法,可以在O(logN)时间内得到答案,这种方法还有一个优点,在计算较大n时显得格外有效。

这里给出快速矩阵幂的模板,可借鉴:

const int N = 2;

struct Matrix {
    int mat[N][N];
    Matrix() {}
    Matrix operator * (const Matrix& b) const {
        Matrix result;
        memset(result.mat, 0, sizeof(result.mat));
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                for (int k = 0; k < N; ++k) {
                    result.mat[i][j] = (result.mat[i][j] + this->mat[i][k] * b.mat[k][j]) ;
                }
            }
        }
        return result;
    }
};

Matrix MatPow(Matrix base, int n)
{
    Matrix result;
    memset(result.mat, 0, sizeof(result.mat));
    for (int i = 0; i < N; ++i) {
        result.mat[i][i] = 1;
    }

    while (n > 0)
    {
        if(n & 1) result = result * base;
        base = base * base;
        n >>= 1;
    }
    return result;
}
View Code

 

算法第一次练习赛指点

第一题就一个简单的斐波那契数列呀,为什么泥萌都开二维数组,明明只要一个51的数组就够了。

第二题开一个数组就好,为什么被误导成开两个数组呢?

第三题也是斐波那契数列,百度一下矩阵求斐波那契,一分钟AC,不过还是建议熟悉矩阵快速幂的求解。

第四题绝对不是什么DP,扫描一遍O(N)解决就ok。

第五题?HINT里有写使用优先队列了,不懂的赶紧学习一下STL吧!

第六题?听说你推出了公式直接求出答案?想一想结束时并不是每个机器人能量都用光就知道了,换个方法吧,比如二分~

 我要去做练习赛!

大家加油~

 

作者: AlvinZH

出处: http://www.cnblogs.com/AlvinZH/

本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2017-10-08 13:09  AlvinZH  阅读(4030)  评论(0编辑  收藏  举报