編程之美2.9:神奇的菲波那契數列
2015-07-25 17:01
351 查看
只要是聽說過遞歸,學過一點數據結構的人都聽過這個數列。其實高二數學課上也有,不過那時候我還在受馬克思主義的薰陶,不知編程爲何物。好了,據說這個數列源於一對繁殖能力特別驚人的兔子。
其實這個就是一個遞推公式:F(n)=⎧⎩⎨01F(n−1)+F(n−2)n = 0;n = 1;n>=2
一般學校都是在將遞歸時用這個當例子,這當然好。面試提也會考到這道題,Facebook有次就靠到了。不過你要是幹用遞歸寫,你也不用進行下一輪面試了。原因只有一個,太慢!!!重複計算太多!!
你看,如果要求F(5),則計算F(4)和F(3),計算F(4)呢,又要再次計算一下F(3),當然慢。而且函數調用還要保存棧指針,傳遞參數等。
比較好一點的人可能想到用個一維數組。這樣就可以把複雜度降低到O(n)了,我也在這裏獻醜了。寫一個我的實現。
好了,这就是传说中的複雜度爲O(N)的方法,比之前的递归是要好一点了。面试的时候写这个也差不多达到平均水平了。下面还有一种方法,虽然理论上可以。就是求数列的通项公式,不过这个引入了无理数。所以实际上不行。
下面是最暴的,也是比较有水平的方法。复杂度可以达到O(Log2N).策略呢,当然是我抄过来的。用矩阵。
因为Fibonacci是二阶递推数列,所以由矩阵相称的公式可得。存在一个2*2的矩阵A。使得(FnFn−1)=(Fn−1Fn−2)∗A
求解可得:A=(1110)
由上面可以推出:(FnFn−1)=(Fn−1Fn−2)∗A=(Fn−2Fn−3)∗A2=....=(F1F0)An−1
看到了没,现在只需要能求出An−1,然后做一个矩阵乘法就行啦。
所以我们要做的工作就是实现一个矩阵操作的类或者函数。OK,我在装下B,写一个简单实现。
上述就是一个简单的实现,不过代码有点小问题。哈,你们自己看着办吧。下篇文章估计写大数计算。正好也实在一下,当菲薄那契数列的项数过大时,无法表示的情况。
参考资料:
<编程之美>书籍
编程之美2.9
其實這個就是一個遞推公式:F(n)=⎧⎩⎨01F(n−1)+F(n−2)n = 0;n = 1;n>=2
一般學校都是在將遞歸時用這個當例子,這當然好。面試提也會考到這道題,Facebook有次就靠到了。不過你要是幹用遞歸寫,你也不用進行下一輪面試了。原因只有一個,太慢!!!重複計算太多!!
你看,如果要求F(5),則計算F(4)和F(3),計算F(4)呢,又要再次計算一下F(3),當然慢。而且函數調用還要保存棧指針,傳遞參數等。
比較好一點的人可能想到用個一維數組。這樣就可以把複雜度降低到O(n)了,我也在這裏獻醜了。寫一個我的實現。
滚动数组法
#include <stdio.h> int arr[3]={0,1}; int main(void){ int index=0; printf("Enter the index:\n"); scanf("%d",&index); if(index<=2){ printf("%d\n",arr[index-1]); return 0; }else{ for(int i=3;i<=index;++i){ arr[2]=arr[1]+arr[0]; arr[0]=arr[1]; arr[1]=arr[2]; } } printf("%d\n",arr[2]); return 0; }
好了,这就是传说中的複雜度爲O(N)的方法,比之前的递归是要好一点了。面试的时候写这个也差不多达到平均水平了。下面还有一种方法,虽然理论上可以。就是求数列的通项公式,不过这个引入了无理数。所以实际上不行。
下面是最暴的,也是比较有水平的方法。复杂度可以达到O(Log2N).策略呢,当然是我抄过来的。用矩阵。
因为Fibonacci是二阶递推数列,所以由矩阵相称的公式可得。存在一个2*2的矩阵A。使得(FnFn−1)=(Fn−1Fn−2)∗A
求解可得:A=(1110)
由上面可以推出:(FnFn−1)=(Fn−1Fn−2)∗A=(Fn−2Fn−3)∗A2=....=(F1F0)An−1
看到了没,现在只需要能求出An−1,然后做一个矩阵乘法就行啦。
所以我们要做的工作就是实现一个矩阵操作的类或者函数。OK,我在装下B,写一个简单实现。
#include <iostream> #include <cassert> using namespace std; const int N=4; class Martix{ public: unsigned int size; long long int data[N*N]; }; //Multi bewteen martix and martix Martix martixMulti(const Martix A,const Martix B,Martix result){ assert(A.size==B.size); result.size=A.size; for(int i=0;i<result.size;++i) for(int j=0;j<result.size;++j){ result.data[i*result.size+j]=0; for(int k=0;k<result.size;++k) result.data[i*result.size+j]+=A.data[i*A.size+k]*B.data[k*B.size+j]; } return result; } //Compute the n-1 of exp Martix martixPow(Martix &A,int index){ if(index==0) exit(0); --index; //compute n-1 exp Martix IdenMartix; IdenMartix.size=2; IdenMartix.data[0]=1,IdenMartix.data[1]=0; IdenMartix.data[2]=0,IdenMartix.data[3]=1; Martix result=IdenMartix; for(;index;index>>=1){ if(index&1) result=martixMulti(result,A,result); A=martixMulti(A,A,A); } return result; } int Fib(int n){ Martix A; A.size=2; A.data[0]=1,A.data[1]=1; A.data[2]=1,A.data[3]=0; Martix an=martixPow(A,n); return an.data[0]; } int main(int agrc,char **argv){ unsigned int index; cout<<"Input index:";cin>>index; cout<<Fib(index)<<endl; return 0; }
上述就是一个简单的实现,不过代码有点小问题。哈,你们自己看着办吧。下篇文章估计写大数计算。正好也实在一下,当菲薄那契数列的项数过大时,无法表示的情况。
参考资料:
<编程之美>书籍
编程之美2.9
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析
- C#获取关键字附近文字算法实例