Fibonacci數列遞迴演算法與非遞迴演算法
阿新 • • 發佈:2018-11-12
轉載於:http://blog.csdn.net/qq_33951180/article/details/52484080
一、斐波那契數列
由於斐波納挈數列是以兔子的繁殖引入的,因此也叫“兔子數列”。它指的是這樣一個數列:0,1,1,2,3,5,8,13……從這組數可以很明顯看出這樣一個規律:從第三個數開始,後邊一個數一定是在其之前兩個數的和。在數學上,斐波納挈數列可以以這樣的公式表示:
F(0) = 0
F(1) = 1 F(n) = F(n-1) + F(n-2),(n>=2)
二、斐波那契數列的實現
既然該數列已經有這樣一個規律:F(n) = F(n-1) + F(n-2);那麼我們很容易就能想到用遞迴的方法,這樣寫出來的程式碼比較簡潔
int fibo1(int n)
{
assert(n >= 0);
if(n == 1 || n == 0) return n;
return fibo1(n - 1) + fibo1(n - 2);
}
當然,我們也可以這樣寫:
int fibo1(int n)
{
assert(n >= 0);
return n < 2 ? n : fibo1(n - 1) + fibo1(n - 2);
}
這樣的遞迴演算法雖然只有簡單的幾行,但是效率卻很低。為什麼呢?我們可以分析其遞迴呼叫的時間複雜度:
時間複雜度 —– O(2^N)
由於使用遞迴時,其執行步驟是:要得到後一個數之前必須先計算出之前的兩個數,即在每個遞迴呼叫時都會觸發另外兩個遞迴呼叫,例如:要得到F(10)之前得先得到F(9)、F(8),那麼得到F(9)之前得先得到F(8)、F(7)……如此遞迴下去
從上圖我們可以看出,這樣的計算是以 2 的次方在增長的。除此之外,我們也可以看到,F(8)和F(7)的值都被多次計算,如果遞迴的深度越深,那麼F(8)和F(7)的值會被計算更多次,但是這樣計算的結果都是一樣的,除了其中之一外,其餘的都是浪費,可想而知,這樣的開銷是非常恐怖的!
所以,如果在時間複雜度和空間複雜度都有要求的話,我們可以用以下兩種非遞迴演算法來實現:
@@:時間複雜度為O(N),空間複雜度為O(N)
建立一個數組,每次將前兩個數相加後直接賦給後一個數。這樣的話,有N個數就建立一個包含N個數的一維陣列,所以空間複雜度為O(N);由於只需從頭向尾遍歷一邊,時間複雜度為O(N)
int* fibo2(int n)
{
assert(n >= 0);
int* pArr = new int[n + 1];
pArr[0] = 0;
pArr[1] = 1;
for(int i = 2; i <= n; i++){
pArr[i] = pArr[i - 1] + pArr[i - 2];
}
return pArr;
}
@@:時間複雜度為O(N),空間複雜度為O(1)
藉助兩個變數 nFirst 和 nSecond ,每次將 nFirst 和 nSecond 相加後賦給 nThird,再將 nSecond 賦給 nFirst ,nThird賦給 nSecond ,如此迴圈。
int fibo3(int n)
{
assert(n >= 0);
int nFirst = 0;
int nSecond = 1;
int nThird = 0;
for(int i = 2 ; i <= n; i++){
nThird = nFirst + nSecond;
nFirst = nSecond;
nSecond = nThird;
}
return nThird;
}
測試
int main()
{
int n;
cout << "請輸入數列項:";
cin >> n;
cout << "斐波那契數列前" << n << "項如下" << endl;
for(int i = 0; i < n; i++){
cout << fibo1(i) << "\t";
}
cout << endl;
int* pArr = fibo2(n);
for(int i = 0; i < n; i++){
cout << pArr[i] << "\t";
}
delete[] pArr; pArr = NULL;
cout << endl;
for(int i = 0; i < n; i++){
cout << fibo3(i) << "\t";
}
cout << endl;
system("pause");
return 0;
}