又稱黃金分割數列,或兔子數列(由於是斐波那契觀察兔子生殖而總結獲得)。面試
在數學上,被定義爲遞推式:F(1)=1, F(2)=1, F(3)=2, F(4)=3, F(5)=5, ..., F(n)=F(n-1)+F(n-2)(n≥3)。算法
#include <stdio.h> #include <time.h> int R[1000]={0}; int main(void){ int N = 50; // int N = 1e6; double start, finish; start = clock(); printf("%lld\n", fib(N)); finish = clock(); printf("%f s", (finish-start)/CLOCKS_PER_SEC); return 0; }
根據它的遞推表達式,很容易想到使用遞歸實現。單元測試
1 int fib_A(int N){ 2 if( N <= 2 ) 3 return 1; 4 int tmp = fib_A(N-1)+fib_A(N-2); 5 printf("%d: %d\n", N, tmp); 6 return tmp; 7 }
代碼很簡單,可是計算很慢!算前50個Fibonacci數須要超過60秒。分析算法,發現不少實例在進行重複計算!學習
如此,根據這一點很容易想到使用查表的形式進行改進。測試
1 long long fib_B(int N, long long* A){ 2 if( A[N] != 0 )//A已初始化爲0 3 return A[N]; 4 5 A[N] = fib_B(N-1, A)+fib_B(N-2, A); 6 if( R[N] == 0 ){ 7 printf("%d: %lld\n", N, A[N]); 8 R[N] = 1; 9 } 10 return A[N]; 11 }
從結果看,僅算前50個改進明顯,事實測試中算前10000個數壓力都不大(不超過1s)。能夠證實獲得比較好的改進。spa
遞歸是一種自上而下的算法思路,所以爲了計算n,會保留n(壓棧)去結算n-1,以此類推,直到碰到遞歸基,佔內存較大(O(n))。而迭代是自下而上,若是能獲得迭表明達式,計算應該是很是快且不佔空間的(O(1))。3d
Fibonacci遞推式:F(n)=F(n-1)+F(n-2)。可知當前F(n)的結果須要根據前兩項獲得,所以咱們可使用兩個變量,一直保存前兩項的值,每次迭代都更新這兩項,一直跌到到須要計算的n。code
1 long long fib_itera(int N){ 2 int i; 3 long long f, g; 4 5 f = 1; g = 1;//第1項、第2項的值 6 for( i=3; i<=N; i++){ 7 f = f + g; 8 g = f - g; 9 printf("%d: %lld\n", i, f); 10 } 11 return f; 12 }
運行速度是跟以前的遞歸B版本差很少。blog
Fibonacci數不單單只是在學習遞歸的時候能夠用到,實際中應該是很普遍的。這裏舉兩個例子。遞歸
1. Fibonacci查找
是對二分查找的longN的常係數進行改進,能夠經過嚴格的證實,使用Fibonacci黃金分割在查找時對數列進行分割會獲得最優的常係數。具體實現與證實與在查找篇給出,這裏暫不提。
2. 爬樓梯
也是比較常見的一種面試題:若是每次爬樓梯只能一次跨一個或兩個臺階,那麼到第n個臺階有多少中走法?語義規定地面算第0個臺階。
第一層臺階:F(1) = 1; 第二層臺階:能夠一步一步,也能夠一次兩步,F(2) = 2;第三層臺階:能夠一、一、1,或一、2,或二、1,F(3) = 3;第四層:一、一、一、1,或一、一、2,或一、二、1,或二、一、1,或二、2,F(4) = 5。
獲得遞推式,F(1) = 1,F(2) = 2,F(n) = F(n-1) + F(n-2)(n>2)。
同理,若是一次能夠跨一、二、3個臺階,也同樣能夠先推理出前幾項的值,而後獲得遞推表達式(變形的Fibonacci數列)。