斐波那契數列(Fibonacci sequence),又稱黃金分割數列,當n趨向於無窮大時,前一項與後一項的比值越來越逼近黃金分割0.618(或者說後一項與前一項的比值小數部分越來越逼近 0.618)。
黃金比例和斐波那契數列的數學意義密切相關,在我們的生活中小到細胞分裂、花瓣的排列紋路,大到人口密度和土地面積測繪,甚至是宇宙星系都有着斐波那契數列的身影。
有關斐波那契數列的數學定理和相關公式請參考斐波那契數列——百度詞條。其中斐波那契數列的排列方式很有趣,前兩項的和等於第三項的和,我們很容易就可以依據這個特點寫出它的遞推公式: 。並且按照遞推公式我們還可以推導出相應的通項公式:
下面讓我們通過幾個示例來了解斐波那契數列的魅力把。
示例1:無窮數列 1,1,2,3,5,13,21,34,55,…, 稱爲 Fibonacci 數列,計算第n位數列。
同樣的,這次先用循環和遞歸做一遍,然後在想辦法進行優化。
#include <iostream>
using namespace std;
/*
題目:無窮數列 1,1,2,3,5,13,21,34,55,..., 稱爲 Fibonacci 數列,計算第n位數列。
*/
// 循環實現
int Fibonacci(int n)
{
/*
1 1 2 5
a=1 b=1
c=a+b=2
a=b b=c
分析:
c 保存兩數之和,a、b向後移動
*/
int a = 1, b = 1, c = 1;
for (int i = 3; i <= n; ++i)
{
c = a + b;
a = b;
b = c;
}
return c;
}
// 遞歸實現
int Fib(int n)
{
if (n <= 2) return 1;
else return Fib(n - 1) + Fib(n-2);
}
int main()
{
for (int i = 1; i < 10; ++i)
{
cout << Fibonacci(i) << " ";
cout << Fib(i) << endl;
}
return 0;
}
分析遞歸效率:
對於循環來講,它使用遞推公式的方式求解,基本上沒有需要優化的地方,除非我們選擇使用通項公式求解,而對於當前的遞歸來說卻還有着一些優化的空間。
遞歸過程分析:如:欲求的第五位斐波那契數,其遞歸調用方式如下
如圖所示:
- 在第一次遞歸時,把求5的問題轉換爲求4的問題和求3的問題
- 在第二次遞歸時,把求4的問題轉換爲求3的問題和求2的問題
- 在第二次遞歸時,把求3的問題轉換爲求2的問題和求1的問題
- 在第三次遞歸時,把求3的問題轉換爲求2的問題和求1的問題
我們發現僅僅是計算第五個斐波那契數就要重複計算兩次求3問題,三次求2問題,兩次求1問題,這些重複的計算顯然是沒有意義的。從我們人的角度來分析問題,我們只需要計算一次“1”、一次“2”、一次“3”、一次“4”,就可以得到“5”的值了。並且我們也確實這樣做了。正如我們所見到的,我們使用循環實現的求斐波那契數就是按照這種邏輯設計的。
前面提到過,通常情況下循環問題可以轉換爲遞歸問題,那麼我們是否可以把循環式的代碼稍加修改變成遞歸式呢?
循環式分析:
在循環中使用了三個變量,a、b 用於保存 、 的值,使用 c 保存相加後的結果,a、b再向後移動一位。整體的過程如下表所示:
1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 |
---|---|---|---|---|---|---|---|---|
a | b | c | ||||||
a | b | c | ||||||
a | b | c | ||||||
a | b | c | ||||||
a | b | c | ||||||
a | b | c | ||||||
a | b | c |
遞歸算法設計:
對於一個 1, 1, 2, 3, 5, 8, 13, 21, 34
的數列,遞歸的求法是將大問題轉化爲小問題後在求解,如:求34問題->求21問題->求13問題->求5問題->求3問題->求2問題。
分析:把這個數列類比成一個數組的話,遞歸就像是倒着數組求值,循環是正向對數組求值。並且在整個過程中都是遍歷完整個數組的,遞歸次數與循環次數相同。
算法:在進行每一步遞歸時同時進行計算,從斐波那契數的起始數 1,1 開始計算,待到遞歸到結束時已經求得斐波那契數值。並且這個算法不存在重複求值問題。
遞歸調用式: ,調用過程中,n爲遞歸次數,a 的位置保存b的值,b 的位置保存 c 的值(a+b)
遞歸過程分析:求第9個斐波那契數
fib(9,1,1) ⇒ fib(8,1,2) ⇒ fib(7,2,3) ⇒ fib(6,3,5) ⇒ fib(5,5,8) ⇒ fib(4,8,13) ⇒ fib(3,13,21) ⇒ fib(2,21,34)
因此,對於遞歸式,的遞歸終止條件也確定了。在 時返回 b ,或者在 返回 a 都可以,下面是C++的代碼實現:
#include <iostream>
using namespace std;
/*
題目:無窮數列 1,1,2,3,5,13,21,34,55,..., 稱爲 Fibonacci 數列,計算第n位數列。
*/
int Fib_Reverse(int n, int a, int b)
{
if (n <= 2) return b;
else return Fib_Reverse(n - 1, b, a + b);
}
int NiceFib(int n)
{
int a = 1, b = 1;
return Fib_Reverse(n, a, b);
}
int main()
{
for (int i = 1; i < 10; ++i)
{
cout << NiceFib(i) << " ";
}
cout << endl;
return 0;
}
除了直接求斐波那契數列的問題外,還有很多其衍生問題。如:求楊輝三角問題、爬樓梯問題、兔子繁殖問題、青蛙跳臺階問題等等。另外,盧卡斯數列 1、3、4、7、11、18…,也具有斐波那契數列同樣的性質。值得一提的是在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用。感興趣的同學可以自行在網上查找相關資料。
最後,如果覺得我的文章對你有幫助的話請幫忙點個贊,你的鼓勵就是我學習的動力。如果文章中有錯誤的地方歡迎指正,有不同意見的同學也歡迎在評論區留言,互相學習。