#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=fn−1+fn−2 ,可以由这个公式,迭代的自低向上找到 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=fn−1+fn−2 ,可得下面的矩阵方程:
(fnfn−1)=(1110)(fn−1fn−2)这个矩阵方程可以化为(fnfn−1)=(1110)n−2(11)设 M为(1110)n−2,整个找第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=PDP−1 (其中D是对角矩阵)将A:
(1110)对角化然后直接求解。 An=PDnP−1 。最后得到 fn=rn−12+rn−22−rn−11−rn−21r2−r1其中r1,r2是矩阵A的特征值 。