zoukankan      html  css  js  c++  java
  • 求解斐波那契数列的第n项

      对于Fibonacci数列,1,1,2,3,5,8,12,...求解第n项值,我们通常用的是递归算法,即递推式f(n) = f(n-1)+f(n-2).然而这其实是一种效率极低的算法,当n达到41时,就已经需要1s左右,随着n的增加,时间是指数级增长的。

      因为该递归算法有太多的重复计算,如下图所示,所用时间T(n) = T(n-1)+T(n-2)+Θ(1),可以知道T(n)有Ω((3/2)n)的下界,T(n)<O(2^n),可以看到这是指数级的时间复杂度。

                 

      具体代码实现如下:

    Elemtype Fibonacci(int n)
    {
        if(n==1||n==2)
            return 1;
        else
            return Fibonacci(n-1)+Fibonacci(n-2);
    }

      基于这种情况,我们首先想到的就是使用迭代法改写,代码如下所示:

    Elemtype Fibonacci_Iterate(int n)
    {
        if(n==1||n==2) return 1;
        Elemtype f1 = 1,f2 = 1;
        for(int j=3;j<=n;++j){
            Elemtype temp = f1+f2;
            f1 = f2;
            f2 = temp;
        }
        return f2;
    }

      迭代法具有O(n)的时间复杂度。

      其实斐波那契数列还有一种O(logn)的算法,那就是用矩阵乘法,如下图所示,该公式可以由归纳法证明。用这个公式求F(n)则可以用分治法,求矩阵的n次方我们只需求n/2次方,然后对其结果再进行一次矩阵乘法。如果n的是奇数,我们就先对n-1做该计算,在将其结果与矩阵matrix进行一次矩阵乘法。假设求F(n)需要时间T(n),T(n) = T(n/2)+Θ(1),所以其时间复杂度为O(logn).

                      

      C语言代码实现如下,每当递归返回时,free掉下一层申请的内存。

    Elemtype matrix[4] = {1,1,1,0};
                        
    Elemtype* MultipMatrix(Elemtype *m1,Elemtype *m2)
    {    
        Elemtype *ret = (Elemtype*)malloc(sizeof(Elemtype)*4);
        ret[0] = m1[0]*m2[0]+m1[1]*m2[2];
        ret[1] = m1[0]*m2[1]+m1[1]*m2[3];
        ret[2] = m1[2]*m2[0]+m1[3]*m2[2];
        ret[3] = m1[2]*m2[1]+m1[3]*m2[3];
        return ret;
    }    
                    
    Elemtype* Fibonacci_Matrix(int n)
    {
        if(n==1) return matrix;
        Elemtype *temp = Fibonacci_Matrix(n >> 1);
        Elemtype *ret = NULL;
        if(n & 0x1){
            ret = MultipMatrix(MultipMatrix(temp,temp),matrix);
    //        return MultipMatrix(MultipMatrix(temp,temp),matrix);
        }
        else{
            ret = MultipMatrix(temp,temp);
    //        return MultipMatrix(temp,temp);        
        }
        if(temp!=matrix)
            free(temp);
        return ret;
    }
    //matrix method

      对于迭代法和矩阵法,当n的规模越大,优势就越明显,当我用如下测试代码,所用时间约为0.17s,而改用迭代法实现的代码,用时达到了4s以上。

      int n = 1000000;
        start = clock();
        for(int i=0;i<1000;++i){
            Elemtype *temp = Fibonacci_Matrix(n);
            printf("%lld
    ",temp[1]);
    //        printf("%lld
    ",Fibonacci(n));
    //        printf("%lld
    ",Fibonacci_Iterate(n));
        }
        end = clock();
        
        double t = ((double)(end-start))/CLK_TCK;
        printf("%f",t);

      总结,如果我们的n的规模不是很大,则可以用迭代法,因为这种算法实现相对容易。当n规模很大时,则最好使用矩阵法,当然,此时的F(n)早已超出了C语言long long的范围,需要想办法另外实现更大整数的表示。

      C语言的动态内存申请和释放使代码容易出错,所以可以考虑使用C++的智能指针来代替,或者定义一个矩阵类,实现=,*等矩阵运算。

  • 相关阅读:
    取代iframe,实现页面中引入别的页面
    axios请求
    接口跨域
    es7,es8
    promise
    移动端开发调试工具神器--Weinre使用方法
    资本论第一卷笔记
    2018春季实习生校招面经(一)阿里篇
    linux小实验-考勤模拟程序
    在基于debian的deepin或者Ubuntu上双等号“==”和双中括号“[[]]”不能使用的真相
  • 原文地址:https://www.cnblogs.com/louwqtc/p/fibonacci.html
Copyright © 2011-2022 走看看