ROSALIND|Rabbits and Recurrence Relations (FIB)
假設兔子需一個月性成熟,,性成熟後每對兔子每個月必繁殖 k 對子代,且不因任何因素死亡,則求 n 月後的兔子對數。
Given: Positive integers n≤40 and k≤5.
Return: The total number of rabbit pairs that will be present after n months, if we begin with 1 pair and in each generation, every pair of reproduction-age rabbits produces a litter of k rabbit pairs (instead of only 1 pair).
(https://rosalind.info/problems/fib/)
背景知識
- Sequence: 數列為項目的集合
- recurrence relation: 數列的後項可為前項所定義
- Fibonacci sequence: 費氏數列乃首項與次項為 1 ,第 n 項為 n - 1 項及 n - 2 項之和的遞迴數列
解題觀念
除了透過歸納既有數列的歸納遞迴關係以外,亦可利用符號輔助思考思考。首先,令 $F_n$ 為第 n 個月的兔子數,$R_n$ 為第 n 個月成熟的兔子數,$r_n$ 為第 n 個月未成熟的兔子數。則
$$ F_n = R_n + r_n \tag{1}$$
是以第 n 個月成熟的兔子數可表示為
$$ R_n = R_{n−1} + r_{n−1} = F_{n-1} \tag{2}$$
又因每一世代可生產 $k$ 對兔子,所以第 n 個月未成熟的兔子數為
$$ r_n = k⋅R_{n−1} \tag{3}$$
結合式子 (1)、 (2) 及 (3) 可得
$$ Fn = R_{n−1} + r_{n−1} + k⋅R_{n−1} \tag{4}$$
由於
$$ R_{n−1} = F_{n−2} \tag{5}$$
$$ F_{n−1} = R_{n−1} + r_{n−1} \tag{6}$$
因此
$$ F_n = F_{n−1} + k⋅F_{n−2} \tag{7}$$
利用 python、R 或 bash 以遞迴方法求費氏數列的形式類似,只是要留意各自的 function 定義方法。
程式實作
Python 實作
1 | def fib(n, k): |
R 實作
1 | fib <- function(n , k) { |
bash 實作
bash 的四則運算寫法可參考 Shell 設計入門:算術運算
1 | fib () { |
複雜度分析
雖然遞迴法的邏輯清晰,但以此法求費氏數列第 n 項的效率甚差。為了計算 $F_n$,得計算 $F_{n-1}$ 和 $F_{n-2}$,而求 $F_{n-1}$ 又需計算 $F_{n-2}$ 和 $F_{n-3}$,其中 $F_{n-2}$ 重複計算了兩次,而重複計算的次數又隨 n 值增加。簡言之,由於每項數列皆衍伸出額外兩項的計算,求解費氏數列的第 n 項需要近 2^n 步驟,所以時間複雜度為 O(2^n)。詳細解說可參考:初學者學演算法|從費氏數列認識何謂遞迴
相較之下,使用迴圈可以 O(n) 求費氏數列第 n 項(下為 python 程式碼)
1 | def FibLoop(n, k): |