題目:定義Fibonacci序列如下:f(0)=0,f(1)=f(2)=1,n>2時,f(n)=f(n-1)+f(n-2) ,輸入n ,用最快的方法求該數列的第 n 項。
分析:
首先遞歸求法肯定都會,但是由於遞推關係的形式,很容易看出裏面有很多的重複計算。改進的方法也很容易想到,即申請額外的兩個空間來存放保存前面的計算結果,以此來提供速度:
參考代碼:
1: int getNthFibonacci(int n) 2: { 3: int first=0,second=1; 4: if(n==0 || n==1) 5: return n; 6: 7: int result=0; 8: for(int i=1;i<n;i++) 9: { 10: result += first+second; 11: first=second; 12: second=result; 13: } 14: return result; 15: }
這裏時間複雜度爲O(n),網上還有一種時間複雜度爲log(n)的算法,思想如下:
其數學基礎:方陣{ f(n), f(n-1), f(n-1), f(n-2) } = {1, 1, 1,0 }n-1
即前者表示一個2X2的方陣,後者表示一個第一行爲1,1、第二行爲1,0的方陣的n-1次方。矩陣{1,1,1,0}的n-1次方的結果的第一行第一列就是f(n),可以用歸納法證明,比較簡單。
現在的問題轉換爲求矩陣{1, 1, 1, 0}的乘方。如果簡單第從0開始循環,n次方將需要n次運算,並不比前面的方法要快。但我們可以考慮乘方的如下性質:
/ an/2*an/2 n爲偶數時
an=
\ a(n-1)/2*a(n-1)/2 n爲奇數時
要求得n次方,我們先求得n/2次方,再把n/2的結果平方一下。如果把求n次方的問題看成一個大問題,把求n/2看成一個較小的問題。這種把大問題分解成一個或多個小問題的思路我們稱之爲分治法。這樣求n次方就只需要logn次運算了。
參考代碼:
1: #include<stdlib.h> 2: #include<stdio.h> 3: #include<string.h> //memcpy 4: 5: void multiply(int A[], int B[], int result[]) 6: { 7: result[0] = A[0]*B[0] + A[1]*B[2]; 8: result[1] = A[0]*B[1] + A[1]*B[3]; 9: result[2] = A[2]*B[0] + A[3]*B[2]; 10: result[3] = A[2]*B[1] + A[3]*B[3]; 11: } 12: 13: void power(int A[], int n, int result[]) 14: { 15: if (n==1) 16: { 17: memcpy(A, result, 4*sizeof(int)); 18: return; 19: } 20: int tmp[4]; 21: power(A, n>>1, result); 22: multiply(result, result, tmp); 23: 24: if (n & 1 == 1) 25: { 26: multiply(tmp, A, result); 27: } 28: else 29: { 30: memcpy(result, tmp, 4*sizeof(int)); 31: } 32: } 33: 34: int getNthFibonacci(int n) 35: { 36: int A[4] = {1,1,1,0}; 37: int result[4]={1,1,1,0}; 38: power(A, n, result); 39: 40: return result[0]; 41: } 42: 43: 44: int main() 45: { 46:int n 47: while(scanf("%d",&n)) 48: { 49: if(n==-1) 50: break; 51: if(n==0 || n==1) 52: printf("n=%d,result=%d\n",n,n); 53: else 54: printf("n=%d,result=%d\n",n,getNthFibonacci(n-1)); 55: } 56: getchar(); 57: return 0; 58: }