算法习题19:Fibonacci数列深入

来源:互联网 发布:小米5s plus 知乎 编辑:程序博客网 时间:2024/05/21 17:04
题目:定义Fibonacci数列如下:   
  / 0 n=0
f(n)= 1 n=1
  \ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项

——————————————————————————————

这个题目大家很熟悉,就是Fibonacci数列,大家第一个想到的就是递归方法,是的,这个我们学数据结构算法的时候用的就是这个方法,代码精简漂亮,可是有没有想过时间和空间上的开销呢?首先这是一个递归,空间上开销不用说,栈会不断积累,时间,当我们计算F(n-1)时候计算了F(n-2),可是递归没有记忆功能,所以计算F(n-2)还是在来一次,这就造成了时间复杂度的上升应该是O(n2)

(1)循环 

所以这里稍微改进下,首先,递归一般都可以采用非递归方式实现,这里我们用循环来实现

long Fibonacci1(long input){long a0 = 1;long a1 = 1;long sum = 0;int i = 0;if(input <2)return 1;for(i=2;i<=input;i++){sum = a0+a1;a0 = a1;a1 = sum;}return sum;}

其实代码不难,重要的是思路把,

后来我上网搜了下大家对这道题的看法,有人提出了更巧妙的方式,循环的时间复杂度我们知道就是O(N)似乎已经很好了,不过还有O(logN)的方法

(2)矩阵

大家可以计算下

| 1  1 |

| 1  0 |

这个矩阵的n次方的规律,我们发现,刚好就是

|f(n-1)+f(n-2)    f(n-1) |

|f(n-1)               f(n-2) |

所以这道题就可以转换成矩阵求幂了。

可以A^n还是需要计算n次啊,我们可以利用平方的性质  [A^(n/2)}^2 = A^n  所以以此类推 那就是logN的复杂度了 不过要注意奇数偶数的幂

偶数    A^n = [A^(n/2)}^2

奇数    A^n = [A^((n-1)/2)}^2*{1,1,1,0}

long Fibonacci3(int input){long arr[4] = {1,1,1,0};long* result = F3(arr, input);return result[0];}long* F3(long* arr, int input){long arr1[4], arr2[4];long* temp1 = arr1;long* temp2 = arr2;if(input == 1){temp1[0] = 1;temp1[1] = 1;temp1[2] = 1;temp1[3] = 0;return temp1;}if(input == 2){//求得平方temp1[0] = arr[0]*arr[0] + arr[1]*arr[2];temp1[1] = arr[0]*arr[1] + arr[1]*arr[3];temp1[2] = arr[2]*arr[0] + arr[3]*arr[2];temp1[3] = arr[2]*arr[1] + arr[3]*arr[3];return temp1;}else{if(input%2 == 0){temp1 = F3(arr, input/2);temp2[0] = temp1[0]*temp1[0] + temp1[1]*temp1[2];temp2[1] = temp1[0]*temp1[1] + temp1[1]*temp1[3];temp2[2] = temp1[2]*temp1[0] + temp1[3]*temp1[2];temp2[3] = temp1[2]*temp1[1] + temp1[3]*temp1[3];return temp2;}else{temp1 = F3(arr, (input-1)/2);temp2[0] = temp1[0]*temp1[0] + temp1[1]*temp1[2];temp2[1] = temp1[0]*temp1[1] + temp1[1]*temp1[3];temp2[2] = temp1[2]*temp1[0] + temp1[3]*temp1[2];temp2[3] = temp1[2]*temp1[1] + temp1[3]*temp1[3];temp1[0] = temp2[0] + temp2[1];temp1[1] = temp2[0] ;temp1[2] = temp2[2] + temp2[3];temp1[3] = temp2[2] ;return temp1;}}cout<<"NULL??";return NULL;}


虽然这种方法依然是一个递归,当时次数从n^2---->logn  这个是一个很大的进步

参见:http://blog.csdn.net/yuucyf/article/details/6625301
(3)递归

为了比较,这里还是把老式方法给出来

long Fibonacci2(long input){if(input < 2)return 1;elsereturn Fibonacci2(input-1)+Fibonacci2(input-2);}


我这里为了不让数列数太大,我循环执行,为了比较效率

long Fibonacci1(long input);long Fibonacci2(long input);long Fibonacci3(int input);long* F3(long* arr, int input);int main() {long input = 0l;cin>>input;int i =0;long result;clock_t start,end;        //循环方法cout<<endl<<"循环:"<<endl;start = clock();for(i=0;i<3000000;i++)result = Fibonacci1(input);cout<<result;end = clock();cout<<endl<<(double)(end-start)/CLOCKS_PER_SEC;        //老式递归cout<<endl<<"递归:"<<endl;start = clock();for(i=0;i<3000000;i++)result = Fibonacci2(input);cout<<result;end = clock();cout<<endl<<(double)(end-start)/CLOCKS_PER_SEC;        //举证方法cout<<endl<<"矩阵:"<<endl;start = clock();for(i=0;i<3000000;i++)result = Fibonacci3(input);cout<<result;end = clock();cout<<endl<<(double)(end-start)/CLOCKS_PER_SEC;return 0;}


输出比较

13循环:3770.18s递归:3779.68s矩阵:3770.26s
我们看到,老式的递归将近10秒了,由于输入10较小所以矩阵的速度无法充分体现出来,这里递归影响较大

我们看到,当输入求50的时候,时间就差开啦

50循环:203650110740.71s矩阵:203650110740.38s



原创粉丝点击