这是我印象最深刻的一道题 因为最开始我并没有考虑使用编程
题目复现一下:
生物学家小 R 正在研究一种特殊的兔子品种的繁殖模式。这种兔子的繁殖遵循以下规律:
- 每对成年兔子每个月会生育一对新的小兔子(一雌一雄)。
- 新生的小兔子需要一个月成长,到第二个月才能开始繁殖。
- 兔子永远不会死亡。
小 R 从一对新生的小兔子开始观察。他想知道在第 A 个月末,总共会有多少对兔子。
请你帮助小 R 编写一个程序,计算在给定的月份 A 时,兔子群体的总对数。
注意:
-
初始时有 1 对新生小兔子。
-
第 1 个月末有 1 对兔子:原来那对变成了成年兔子,并开始繁殖。
-
第 2 个月末有 2 对兔子:原来那 1 对成年兔子,繁殖了 1 对新生的小兔子。
-
从第 3 个月开始,兔子群体会按照上述规律增长。
我一开始我并没有把他当成一道编程题去做,而是先想成数学问题。
于是就开始了数学的解题过程,看看能不能构建函数,或者列表的方法找规律。
我最后还真去列了个表:
一开始的想法是算第一对兔子在30天中他们的第一代子代 和他们一共可以生出多少字兔子 然后考虑第二个子代。然后我发现不对 这脑子都给绕晕了,于是换了种方法,就是把这月中哪天的兔子群体的总对数去算
于是有了下面这个表(当时写在纸上,这是我用ai生成的 ):
这个时候其实我还是没发现什么规律,无奈求组使用AI助手。
AI助手直接说:
这个问题实际上是一个经典的斐波那契数列问题。斐波那契数列的定义是:F(0) = 0F(1) = 1F(n) = F(n-1) + F(n-2) 对于 n >= 2在这个兔子繁殖问题中,第 A 个月末的兔子对数实际上就是斐波那契数列的第 A 项。
斐波那契数列?我印象里面只是在一些科普文里面出现过,说自然界有很多地方都有这个,叫做又称黄金分割数列。
而后我也学习到了很多这方面的知识,斐波那契数列也是算法中的一道常考题,这里先给出这题的解。
这题有两种解法:
递归算法 和 **动态规划
**
首先说说递归:
斐波那契数列这题的本质在数学上来说是递推得到的规律,虽然在本质上递归也是基于递推规律,但从程序实现角度来看有不同思路。
下面是TS的解题:
function fibonacciRecursive(n: number): number {
if (n === 0 || n === 1) {
return n;
}
return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
}
// 示例用法
let month = 10;
let resultRecursive = fibonacciRecursive(month);
console.log(`第${month}个月兔子的总对数(递归算法): ${resultRecursive}`);
在上述代码中:
fibonacciRecursive函数接受一个参数n,表示要计算的斐波那契数列的第n项(对应第n个月末兔子的总对数)。- 如果
n为 0 或 1,直接返回n,因为斐波那契数列的前两项是 0 和 1。 - 对于其他情况,通过递归调用自身,分别传入
n - 1和n - 2,按照斐波那契数列的递推关系(每一项等于前两项之和)来计算并返回结果。
**再来说说看动态规划
**我个人认为动态规划在这题中从代码上可读性最强也是最好理解的方法
动态规划旨在解决有重叠子问题和最优子结构性质的问题,兔子繁殖问题符合这一特征。
function fibonacciDynamicProgramming(n: number): number {
if (n === 0 || n === 1) {
return n;
}
let dp: number[] = new Array(n + 1);
dp[0] = 0;
dp[1] = 1;
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// 示例用法
let monthDP = 10;
let resultDP = fibonacciDynamicProgramming(monthDP);
console.log(`第${monthDP}个月兔子的总对数(动态规划算法): ${resultDP}`);
在这段代码里:
-
fibonacciDynamicProgramming函数同样用于计算斐波那契数列的第n项。 -
首先判断
n为 0 或 1 的情况并返回相应值。 -
然后创建一个长度为
n + 1的数组dp,用于存储计算过程中的中间结果,初始化dp[0]和dp[1]。 -
通过循环,从第 2 项开始,根据已经存储在
dp数组中的前两项的值(dp[i - 1]和dp[i - 2]),按照斐波那契数列的递推关系计算出当前项的值并存入dp[i]。 -
最后返回
dp[n],即第n个月末兔子的总对数。
动态规划算法通过避免递归算法中的重复计算,在计算较大的 n 值时,通常会比递归算法具有更高的效率。
这就是我解答这道题的过程。也是通过这道题我理解到,斐波那契数列在算法的学习中也是一类非常常见的题目,他可以用各种方法来考,需要你灵活的时候所学习的知识用于解答。这也是我初次接触动态规划这种解题方法,可以说收获颇丰。