Rabbits and Recurrence Relations (ID: FIB)

Problem

假設兔子需一個月性成熟,,性成熟後每對兔子每個月必繁殖 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).

Background

  • Sequence: 數列為項目的集合
  • recurrence relation: 數列的後項可為前項所定義
  • Fibonacci sequence: 費氏數列乃首項與次項為 1 ,第 n 項為 n - 1 項及 n - 2 項之和的遞迴數列

Solution

除了透過歸納既有數列的歸納遞迴關係以外,亦可利用符號輔助思考思考。首先,令 $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

def Fib(n, k):
    if n <= 2:
        return 1
    else:
        return Fib(n - 1, k) + Fib(n - 2, k) * k
print Fib(5, 3)

R

Fib <- function(n , k) {
  if (n <= 2) {
    return(1)
  } else {
    return(Fib(n - 1, k) + Fib(n - 2, k) * k)
  }
}
print(Fib(5, 3))

bash

bash 的四則運算寫法可參考 Shell 設計入門:算術運算

Fib () {
    n=$1
    k=$2
    if [[ $n -le 2 ]]
    then
         echo 1
    else
         echo $[$(Fib $[n-1] $k) + $[$(Fib $[n-2] $k) * $k]]
    fi
}
Fib 5 3

Discussion


時間複雜度分析

雖然遞迴法的邏輯清晰,但以此法求費氏數列第 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 程式碼)

def FibLoop(n, k):
    s = [1, 1]
    i = 2
    while i < n:
        s.append(s[i - 1] + s[i - 2] * k)
        i += 1
    return s[n - 1]

print FibLoop(5, 3)

沒有留言:

張貼留言

Back to top