斐波那契数列

题目一:写一个函数,输入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;
}

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

posted @ 2015-09-11 17:32  taxue505  阅读(318)  评论(0编辑  收藏  举报