• 斐波那契数列


    题目一:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。斐波那契数列数列的定义如下:

    f[n]=f[n-1]+f[n-2],且f[1]=1,f[2]=1。

    除了面试官直接要求编程实现斐波那契数列之外,还有不少面试题可以看成是斐波那契数列的应用,比如:

    题目二:一只青蛙可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级台阶总共有多少种跳法。

    首先我们考虑最简单的情况。如果只有一级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳法了:一种是分两次跳,没跳1级;另外一种就是一次跳2级。

    接着我们再来讨论一般情况。我们把n级台阶时的跳法看成时n的函数,记为f(n)。当n>2时,第一次跳的时候就有两种不同的选择:

    (1)第一次只挑1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);

    (2)选择第一次跳两级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。

    因此n级台阶的不同跳法的总数f(n) = f(n-1) + f(n-2)。分析到这里我们不难看出这实际上就是斐波那契数列了。

    类似的题目还有骨牌覆盖问题,算法-骨牌覆盖问题(矩阵快速幂求Fibonacii)

    效率很低的递归算法

    我们的教科书上反复用这个问题来讲解递归函数,并不能说明递归的解法最适合这道题目。

    long long Fibonacci(unsigned int n)
    {
        if(n <= 0){
            return 0;
        }
        if(n == 1){
            return 1;
        }
        return Fibonacci(n-1) + Fibonacci(n-2);
    }

    优点:形式简洁,便于人理解

    缺点:虽说递归法以空间换时间,但一旦n变大,它的速度的确比非递归法慢得多,例如对n=40,测试递归法所用时间为8~9s;而非递归法只需要远不到1s。

    原因是,递归过程中,系统建立堆栈来保存上一层状态信息, 和退栈获取还原系统状态都要有开销的.系统做的事情不少, 所以效率要低.

    例如,f(5)的话,实际上会调用这个函数15次,有15个临时栈区,试想f(100)有多吓人。。。一般来说到f(40)以后,普通计算机基本上就不可能计算出来了。

    通项公式算法

    引用块内容

    //使用通项公式Fib(n) = [(1+√5)/2]^n /√5 - [(1-√5)/2]^n /√5
    
    #define ROOT_OF_FIVE sqrt(5.0)
    
    long double Fibonacci(unsigned int n)
    {
        return (pow(((1 + ROOT_OF_FIVE) / 2.0),n) / ROOT_OF_FIVE - 
            pow(((1 - ROOT_OF_FIVE) / 2.0),n) / ROOT_OF_FIVE);
    }

    迭代算法

    上述递归代码之所以慢是因为重复计算太多,我们只要想办法避免重复计算就行了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过就不用再重复计算了。

    更简单的方法是从下网上算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3)…….以此类推就可以算出第n项了。很容易理解,这种思路的时间复杂度是O(n)。

    #include <iostream>
    using namespace std;
    
    long long Fibonacci(unsigned n)
    {
        int result[2] = {0,1};
        if(n < 2)
            return result[n];
        long long fibNMinusOne = 1;
        long long fibNMinusTwo = 0;
        long long fibN = 0;
        for(unsigned int i = 2; i <= n; i++){
            fibN = fibNMinusOne + fibNMinusTwo;
    
            fibNMinusTwo = fibNMinusOne;
            fibNMinusOne = fibN;
        }
        return fibN;
    }
    int main()
    {
        int n;
        while(cin>>n){
            cout<<Fibonacci(n)<<endl;
        }
        system("pause");
        return 0;
    }

    矩阵快速幂算法

    时间复杂度O(logn)但不够实用的解法

    程序1:

    //2*2矩阵结构体
    struct Matrix2by2
    {
    //构造函数
        Matrix2by2(long long m_00, long long m_01, long long m_10, long long m_11) :
            m00(m_00), m01(m_01), m10(m_10), m11(m_11){}
    
    //数据成员
        long long m00;
        long long m01;
        long long m10;
        long long m11;
    };
    
    //两个2*2矩阵乘法
    Matrix2by2 Matrix_Multiply(const Matrix2by2 matrix1, const Matrix2by2 matrix2)
    {
        //初始化矩阵
        Matrix2by2 matrix12(1, 1, 1, 0);
    
        matrix12.m00 = matrix1.m00*matrix2.m00 + matrix1.m01*matrix2.m10;
        matrix12.m01 = matrix1.m00*matrix2.m01 + matrix1.m01*matrix2.m11;
        matrix12.m10 = matrix1.m10*matrix2.m00 + matrix1.m11*matrix2.m10;
        matrix12.m11 = matrix1.m10*matrix2.m01 + matrix1.m11*matrix2.m11;
    
        return matrix12;
    }
    
    //矩阵的快速幂算法
    Matrix2by2 Matrix_Fast_Power(unsigned int n)
    {
        //基矩阵
        Matrix2by2 matrix(1, 1, 1, 0);
    
        if (n == 1)
            matrix = Matrix2by2(1, 1, 1, 0);
        else if (n % 2 == 0)
        {
            matrix = Matrix_Fast_Power(n / 2);
            matrix = Matrix_Multiply(matrix, matrix);
        }
        else if (n % 2 == 1)
        {
            matrix = Matrix_Fast_Power((n - 1) / 2);
            matrix = Matrix_Multiply(matrix, matrix);
            matrix = Matrix_Multiply(matrix, Matrix2by2(1, 1, 1, 0));
        }
    
        return matrix;
    }
    
    //斐波那契数列计算式
    long long P_Fibonacci(unsigned int n)
    {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
    
        Matrix2by2 Fib_Matrix = Matrix_Fast_Power(n - 1);
    
        return Fib_Matrix.m00;
    
    }
    
    

    程序2:

    #include <iostream>
    using namespace std;
    
    const int MOD = 19999997;
    struct matrix{       //重载结构体
    public:
        long long a;
        long long b;
        long long c;
        long long d;
        matrix &operator * (matrix &data){ //重载*
            long long tempa = a;
            long long tempb = b;
            long long tempc = c;
            long long tempd = d;
            a = tempa*data.a%MOD+tempb*data.c%MOD;
            b = tempa*data.b%MOD+tempb*data.d%MOD;
            c = tempc*data.a%MOD+tempd*data.c%MOD;
            d = tempc*data.b%MOD+tempd*data.d%MOD;
            return *this;
        }
        matrix &operator = (matrix &data){  //重载&
            a = data.a;
            b = data.b;
            c = data.c;
            d = data.d;
            return *this;
        }
    };
    
    long long fastFibonacci(long n)
    {
        matrix res = {1,1,1,0};
        matrix base = {1,1,1,0};
        matrix temp;
        n++;
        while(n){
            if(n&1){
                res = res*base;
            }
            temp = base;
            base = base*temp;
            n >>=1;
        }
        return res.d;
    }
    int main()
    {
        long n;
        while(cin>>n){
            cout<<fastFibonacci(n)%MOD<<endl;
        }
        system("pause");
        return 0;
    }

    解法比较

    用不同的方法求解斐波那契数列的时间效率大不相同。

    使用哪种方法根据实际情况确定,从时间复杂度上来说O(通向公式法)《O(快速幂法)《O(迭代法)《O(递归法)。

    第一种基于递归的解法虽然直观但是时间效率很低,在实际软件开发中不会用这种方法,也不可能得到面试官的青睐。

    第二种防方法结果不会很准确,但至少能说明问题。

    第三种方法把递归的算法用循环实现,极大地提高了时间效率。

    第四种方法把斐波那契数列转换成求矩阵的乘方,是一种很有创意的算法。虽然我们可以用O(logn)求得矩阵的n次方,但由于隐含的时间常数较大,很少会有软件采用这种算法。另外实现这种解法的代码也很复杂,不太适用面试。因此第三种方法不是一种实用的算法,不过应聘者可以用它来展现自己的知识面。

    完整的程序代码:

    #include <iostream>
    
    #define ROOT_OF_FIVE sqrt(5.0)
    
    
    using namespace std;
    
    //递归方式
    long long R_Fibonacci(unsigned int n)
    {
        if (n <= 0)
            return 0;
        if (n == 1)
            return 1;
        return R_Fibonacci(n - 1) + R_Fibonacci(n - 2);
    }
    
    //通项公式算法
    
    long double T_Fibonacci(unsigned int n)
    {
        return (pow(((1 + ROOT_OF_FIVE) / 2.0), n) / ROOT_OF_FIVE -
            pow(((1 - ROOT_OF_FIVE) / 2.0), n) / ROOT_OF_FIVE);
    }
    
    //迭代方式
    long long I_Fibonacci(unsigned int n)
    {
        int result[2] = { 0, 1 };
        if (n < 2)
            return result[n];
    
        long long Fib_One = 0;
        long long Fib_Two = 1;
        long long FibN = 0;
    
        for (unsigned int i = 2; i <= n; ++i)
        {
            FibN = Fib_One + Fib_Two;
    
            Fib_One = Fib_Two;
            Fib_Two = FibN;
    
        }
    
        return FibN;
    }
    
    //矩阵快速幂算法
    
    //2*2矩阵结构体
    struct Matrix2by2
    {
    //构造函数
        Matrix2by2(long long m_00, long long m_01, long long m_10, long long m_11) :
            m00(m_00), m01(m_01), m10(m_10), m11(m_11){}
    
    //数据成员
        long long m00;
        long long m01;
        long long m10;
        long long m11;
    };
    
    //两个2*2矩阵乘法
    Matrix2by2 Matrix_Multiply(const Matrix2by2 matrix1, const Matrix2by2 matrix2)
    {
        //初始化矩阵
        Matrix2by2 matrix12(1, 1, 1, 0);
    
        matrix12.m00 = matrix1.m00*matrix2.m00 + matrix1.m01*matrix2.m10;
        matrix12.m01 = matrix1.m00*matrix2.m01 + matrix1.m01*matrix2.m11;
        matrix12.m10 = matrix1.m10*matrix2.m00 + matrix1.m11*matrix2.m10;
        matrix12.m11 = matrix1.m10*matrix2.m01 + matrix1.m11*matrix2.m11;
    
        return matrix12;
    }
    
    //矩阵的快速幂算法
    Matrix2by2 Matrix_Fast_Power(unsigned int n)
    {
        //基矩阵
        Matrix2by2 matrix(1, 1, 1, 0);
    
        if (n == 1)
            matrix = Matrix2by2(1, 1, 1, 0);
        else if (n % 2 == 0)
        {
            matrix = Matrix_Fast_Power(n / 2);
            matrix = Matrix_Multiply(matrix, matrix);
        }
        else if (n % 2 == 1)
        {
            matrix = Matrix_Fast_Power((n - 1) / 2);
            matrix = Matrix_Multiply(matrix, matrix);
            matrix = Matrix_Multiply(matrix, Matrix2by2(1, 1, 1, 0));
        }
    
        return matrix;
    }
    
    //斐波那契数列计算式
    long long P_Fibonacci(unsigned int n)
    {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
    
        Matrix2by2 Fib_Matrix = Matrix_Fast_Power(n - 1);
    
        return Fib_Matrix.m00;
    
    }
    
    int main()
    {
        int n;
        while (cin >> n)
        {
    
            cout << I_Fibonacci(n) << endl; 
            cout << T_Fibonacci(n) << endl;
            cout << P_Fibonacci(n) << endl;
            cout << R_Fibonacci(n) << endl;
        }
    
        system("pause");
        return 0;
    }

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    UnityVS(Visual Studio Tools For Unity)的安装与使用
    Balsamiq Mockups注册码
    python基础之os.system函数
    jenkins配置记录(1)--添加用户权限
    chromedriver与chrome各版本及下载地址
    高阶面试官应掌握哪些面试技巧
    [面试技巧]16个经典面试问题回答思路
    自动代码质量分析(GitLab+JenKins+SonarQube)
    Git提交代码自动触发JenKins构建项目
    Allure 安装及使用
  • 原文地址:https://www.cnblogs.com/yangquanhui/p/4937468.html
Copyright © 2020-2023  润新知