兔子的繁殖问题

意大利著名数学家 Fibonacci 曾提出一个问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子。小兔子长到第 3 个月后每个月又生一对兔子。按此规律,假设没有兔子死亡,第一个月有一对刚出生的小兔子,问第 n 个月有多少对兔子?

Fibonacci

这便是经典的斐波那契数列,因以兔子繁殖为例子而引入,也称为兔子数列,在种子数字 0 和 1 之后,后续的每一个数字都是前面两个数字之和,数列的当前数字与前一个数字的比值无限趋近于黄金分割数 1.61803398875…,故又称为黄金分割数列,0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987

在数学上,斐波那契数列是以递归的方法定义的:

1
2
3
F0=0
F1=1
Fn=F(n-1)+F(n-2)(n>=2,n∈N*)

初始时刻第 0 个月,没有兔子,即 F(0) = 0 对应没有兔子,第一个月,有一对刚出生的兔子,即 F(1) = 1 对应一对兔子,第二个月,这对兔子还是太小,不会生育,仍然只有一对兔子,从第三个月开始,每个月都会多一对新的兔子,而前两个月的兔子也可以生育,所以兔子的数量就按照斐波那契数列的规律增长,经过 n 个月后,有 F(n) 对兔子

  • 递归
1
2
3
4
5
6
7
8
function fibonacci(n) {
if(n === 0 || n === 1) {
return n
}
return fibonacci(n - 2) + fibonacci(n - 1)
}

fibonacci(6) // 第 6 个月有 8 对兔子
  • 动态规划
1
2
3
4
5
6
7
8
9
10
11
12
function fibonacci(n) {
if(n === 0 || n === 1) {
return n
}
const fib = [0, 1]
for (let i = 2; i <= n; i++) {
fib[i] = fib[i - 1] + fib[i - 2]
}
return fib[n]
}

fibonacci(6)
  • 矩阵乘法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function matrixMultiply(a, b) {
const result = []
for (let i = 0; i < a.length; i++) {
result[i] = []
for (let j = 0; j < b[0].length; j++) {
result[i][j] = 0
for (let k = 0; k < a[0].length; k++) {
result[i][j] += a[i][k] * b[k][j]
}
}
}
return result;
}

function matrixPower(matrix, n) {
if (n === 1) {
return matrix
}
if (n % 2 === 0) {
const halfPower = matrixPower(matrix, n / 2)
return matrixMultiply(halfPower, halfPower)
} else {
const halfPower = matrixPower(matrix, (n - 1) / 2)
return matrixMultiply(matrix, matrixMultiply(halfPower, halfPower))
}
}

function fibonacci(n) {
if (n <= 1) {
return n
}
const baseMatrix = [[1, 1], [1, 0]]
const poweredMatrix = matrixPower(baseMatrix, n - 1)
return poweredMatrix[0][0]
}
  • 数学公式

使用斐波那契数列通项公式求解。

1
2
3
4
5
6
function fibonacci(n) {
const sqrt5 = Math.sqrt(5)
const a = (1 + sqrt5) / 2
const b = (1 - sqrt5) / 2
return Math.round((Math.pow(a, n) - Math.pow(b, n)) / sqrt5)
}

注:使用 Canvas 画出斐波那契螺旋线