Fibonacci数——求第n个斐波拉契数

Fibonacci数——求第n个斐波拉契数

#include<iostream>
#include<cmath>

#include<ctime>

using namespace std;

time_t begin_,end_;


//递归版本一
long Fibonacci_rec_v1(long const n){

    if (n == 1 || n == 2) return 1L;

    return Fibonacci_rec_v1(n-1) + Fibonacci_rec_v1(n-2);

}

//递归版本二 动态规划
long *FibonacciNum;
bool *isdone;
long Fibonacci_rec_v2(long const n){

    if (isdone[n]) return FibonacciNum[n];

    isdone[n] = true;
    FibonacciNum[n] = Fibonacci_rec_v2(n - 1) + Fibonacci_rec_v2(n - 2);
    return FibonacciNum[n]; 
}


//非递归版本一
long Fibonacci_norec_v1(long const n){

    long fnmins1(1l);
    long fnmins2(1l);
    long temp;

    for (int i(2); i < n; i++){

        temp = fnmins1;
        fnmins1 += fnmins2;
        fnmins2 = temp;
    }

    return fnmins1;
}

//递归求乘方
void _2x2MatrixCalc(long const a11, long const a12,
                    long const a21, long const a22,

                    long const c11, long const c12,
                    long const c21, long const c22,

                    long &b11, long &b12, long &b21, long &b22)

{

    b11 = a11*c11 + a12*c21;
    b12 = a11*c12 + a12*c22;
    b21 = a21*c11 + a22*c21;
    b22 = a21*c12 + a22*c22;
}

//求乘方问题
void M_helper(long const, long const, long const, long const,
              long &b11, long &b12, long &b21, long &b22,
              long const n){

    if (n == 1){ b11 = 1, b12 = 1; b21 = 1, b22 = 0; return; }
    if (n % 2 == 0) {
        M_helper(1, 1, 1, 0, b11, b12, b21, b22, n >> 1);
        long t11(b11), t12(b12), t21(b21), t22(b22);
        _2x2MatrixCalc (t11, t12, t21, t22,
                        t11, t12, t21, t22,
                        b11, b12, b21, b22
                        );
        return;
    }

    M_helper(1, 1, 1, 0, b11, b12, b21, b22, n >> 1);
    long t11(b11), t12(b12), t21(b21), t22(b22);
    _2x2MatrixCalc (t11, t12, t21, t22,
                    t11, t12, t21, t22,
                    b11, b12, b21, b22
                    );
    t11 = b11, t12 = b12, t21 = b21, t22 = b22;
    _2x2MatrixCalc (1, 1, 1, 0,
                    t11, t12, t21, t22,
                    b11, b12, b21, b22
                    );

}

//非递归版本二
long Fibonacci_norec_v2(long const n){

    long b11, b12, b21, b22;
    M_helper(1,1,1,0,b11, b12, b21, b22,n-2);

    return b11+b12;
}


//非递归版本三---存在精度问题。
long Fibonacci_norec_v3(long const n){

    double r1 = (1 - sqrt(5))/2;
    double r2 = (1 + sqrt(5))/2;

    return  (pow(r2, n - 1) + pow(r2, n - 2) - pow(r1, n - 1) - pow(r1, n - 2)) / (r2 - r1);
}



int main(){

    long n;
    cout << "输入序数"<<endl;
    cin >> n;

    begin_ = clock();
    cout<<"Fibonacci_rec_v1结果:"<<Fibonacci_rec_v1(n)<<endl;
    end_ = clock();
    cout << "time:" << (double)(end_ - begin_) << "miliseconds"<<endl;

    begin_ = clock();
    FibonacciNum = new long[n+1];
    isdone = new bool[n+1];
    FibonacciNum[1] = 1; FibonacciNum[2] = 1;
    for (int i(3); i <= n; i++)isdone[i] = false;
    cout << "Fibonacci_rec_v2结果:" << Fibonacci_rec_v2(n) << endl;
    end_ = clock();
    cout << "time:" << (double)(end_ - begin_) << "miliseconds" << endl;

    begin_ = clock();
    cout << "Fibonacci_norec_v1结果:" << Fibonacci_norec_v1(n) << endl;
    end_ = clock();
    cout << "time:" << (double)(end_ - begin_) << "miliseconds" << endl;

    begin_ = clock();
    cout << "Fibonacci_norec_v2结果:" << Fibonacci_norec_v2(n) << endl;
    end_ = clock();
    cout << "time:" << (double)(end_ - begin_) << "miliseconds" << endl;

    begin_ = clock();
    cout << "Fibonacci_norec_v3结果:" << Fibonacci_norec_v3(n) << endl;
    end_ = clock();
    cout << "time:" << (double)(end_ - begin_) << "miliseconds" << endl;

    system("pause");
}

算法说明:

//递归版本一
long Fibonacci_rec_v1(long const n){

    if (n == 1 || n == 2) return 1L;

    return Fibonacci_rec_v1(n-1) + Fibonacci_rec_v1(n-2);

}

这个版本直接由Fibonacci的定义得到,重复计算的子问题比较多,效率非常低。

//递归版本二 动态规划
long *FibonacciNum;
bool *isdone;
long Fibonacci_rec_v2(long const n){

    if (isdone[n]) return FibonacciNum[n];

    isdone[n] = true;
    FibonacciNum[n] = Fibonacci_rec_v2(n - 1) + Fibonacci_rec_v2(n - 2);
    return FibonacciNum[n]; 
}

这个版本是对上面那个版本的改进,用一对数组记录已经计算过了的子问题,这样就大大加快了算法的效率。


//非递归版本一
long Fibonacci_norec_v1(long const n){

    long fnmins1(1l);
    long fnmins2(1l);
    long temp;

    for (int i(2); i < n; i++){

        temp = fnmins1;
        fnmins1 += fnmins2;
        fnmins2 = temp;
    }

    return fnmins1;
}

fn=fn1+fn2 ,可以由这个公式,迭代的自低向上找到 fn ,通过子问题累计到问题的解,没有重复计算的子问题,效率非常高。

//非递归版本二
long Fibonacci_norec_v2(long const n){

    long b11, b12, b21, b22;
    M_helper(1,1,1,0,b11, b12, b21, b22,n-2);

    return b11+b12;
}

由公式 fn=fn1+fn2 ,可得下面的矩阵方程:

(fnfn1)=(1110)(fn1fn2)
这个矩阵方程可以化为
(fnfn1)=(1110)n2(11)
M
(1110)n2
,整个找第n个Fibonacci数的算法就转化为了求矩阵 M。找 M的算法就和数值自乘方求解的问题如出一辙,可以用递归的算法实现,还可以在递归的基础上用动态规划的方法提高求解效率,更可以用迭代的方式来求解。


//非递归版本三---存在精度问题。
long Fibonacci_norec_v3(long const n){

    double r1 = (1 - sqrt(5))/2;
    double r2 = (1 + sqrt(5))/2;

    return  (pow(r2, n - 1) + pow(r2, n - 2) - pow(r1, n - 1) - pow(r1, n - 2)) / (r2 - r1);
}

可以根据矩阵对角化的公式 A=PDP1 (其中D是对角矩阵)将A:

(1110)
对角化然后直接求解。 An=PDnP1 。最后得到 fn=rn12+rn22rn11rn21r2r1r1,r2A

你可能感兴趣的:(递归)