经典的斐波那契数列 - 兔群繁殖之谜 题目解析 | 豆包MarsCode AI刷题

194 阅读4分钟

1. 问题回顾

问题描述

生物学家小 R 正在研究一种特殊的兔子品种的繁殖模式。这种兔子的繁殖遵循以下规律:

  1. 每对成年兔子每个月会生育一对新的小兔子(一雌一雄)。
  2. 新生的小兔子需要一个月成长,到第二个月才能开始繁殖。
  3. 兔子永远不会死亡。

小 R 从一对新生的小兔子开始观察。他想知道在第 A 个月末,总共会有多少对兔子。

请你帮助小 R 编写一个程序,计算在给定的月份 A 时,兔子群体的总对数。

注意:

  • 初始时有 1 对新生小兔子。
  • 第 1 个月末有 1 对兔子:原来那对变成了成年兔子,并开始繁殖。
  • 第 2 个月末有 2 对兔子:原来那 1 对成年兔子,繁殖了 1 对新生的小兔子。
  • 从第 3 个月开始,兔子群体会按照上述规律增长。

输入

一个整数 A(1 ≤ A ≤ 50),表示月份数。

返回

一个长整数,表示第 A 个月末兔子的总对数。

测试样例

样例1:

输入:A = 1
返回:1

样例2:

输入:A = 5
返回:8

样例3:

输入:A = 15
返回:987

2. 问题分析

这个问题实际上是一个经典的斐波那契数列问题。我们可以通过以下步骤来分析和解决这个问题:

  1. 初始状态

    • 第 0 个月(初始状态):1 对新生小兔子。
    • 第 1 个月:1 对成年兔子(原来那对新生小兔子变成了成年兔子)。
  2. 繁殖规律

    • 每对成年兔子每个月会生育一对新的小兔子。
    • 新生的小兔子需要一个月成长,到第二个月才能开始繁殖。
  3. 递推关系

    • 第 n 个月的兔子总数等于第 n-1 个月的兔子总数加上第 n-2 个月的兔子总数。
    • 这是因为第 n-1 个月的兔子在第 n 个月仍然是兔子,而第 n-2 个月的兔子在第 n 个月变成了成年兔子并生育了新的小兔子。
  4. 斐波那契数列

    • 通过上述递推关系,我们可以发现这个问题实际上是斐波那契数列的变种。
    • 斐波那契数列的递推公式为:F(n) = F(n-1) + F(n-2),其中 F(0) = 0F(1) = 1
    • 在这个问题中,初始条件稍有不同:F(0) = 1F(1) = 1

3. 递归法

递归法实现

递归法的实现非常直观,直接使用斐波那契数列的递推公式 F(n) = F(n-1) + F(n-2),并设置初始条件 F(1) = 1 和 F(2) = 1

代码实现

    # 基本情况
    if A == 1:
        return 1
    elif A == 2:
        return 2
    # 递归情况
    else:
        return fibonacci(A - 1) + fibonacci(A - 2)

# 测试样例
print(fibonacci(1))  # 输出: 1
print(fibonacci(5))  # 输出: 8
print(fibonacci(15)) # 输出: 987

复杂度分析

  • 时间复杂度:O(2^A),因为每次递归调用会生成两个新的递归调用,导致指数级的时间复杂度。
  • 空间复杂度:O(A),因为递归调用会使用系统栈,栈的深度最大为 A

4. 矩阵法

斐波那契数列的递推关系可以通过矩阵乘法来表示。具体来说,斐波那契数列的递推关系可以表示为:

(F(n)F(n1))=(1110)(F(n1)F(n2))\begin{pmatrix} F ( n ) \\ F ( n - 1 ) \end{pmatrix} = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} F ( n - 1 ) \\ F ( n - 2 ) \end{pmatrix}

我们可以将这个矩阵表示为 M

M=(1110)M = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}

通过矩阵乘法,我们可以得到:

(F(n)F(n1))=Mn1(F(1)F(0))\left(\begin{array}{cc} F(n) \\ F(n - 1) \\ \end{array}\right) = M^{n - 1}\left(\begin{array}{cc} F(1) \\ F(0) \\ \end{array}\right)

因此,计算斐波那契数列的第 A 项等价于计算矩阵 M 的 A-1 次幂。

矩阵快速幂

为了高效地计算矩阵的幂,我们可以使用快速幂算法。快速幂算法的时间复杂度为 O(log A)。

复杂度分析

  • 时间复杂度:O(log A),因为快速幂算法的时间复杂度为 O(log A)。
  • 空间复杂度:O(1),因为我们只使用了常数个额外的变量来存储矩阵。

5. 个人方法 - 原地数组变化

代码:

#include <iostream>
#include <vector>

long long fibonacci(int a) {
    long long result = -1;
    if (a == 1) {
        return 1;
    } else if (a == 2) {
        return 2;
    } else {
        std::vector<long long> vec = {1, 2};
        long long count = a - 2;
        while (count--) {
            result = vec[0] +vec[1];
            vec[0] = vec[1];
            vec[1] = result;
        }
    }
    return result;
}

long long solution(int A) {
    return fibonacci(A);
}


代码逻辑:

  1. 基本情况处理

    • 如果 a == 1,直接返回 1。
    • 如果 a == 2,直接返回 2。
  2. 初始化数组

    • 创建一个包含两个元素的数组 vec,初始值为 {1, 2},分别表示斐波那契数列的前两项。
  3. 循环计算

    • 计算需要迭代的次数 count = a - 2
    • 使用 while 循环进行迭代,每次迭代计算当前的斐波那契数 result = vec[0] + vec[1]
    • 更新数组 vec,将 vec[1] 赋值给 vec[0],将 result 赋值给 vec[1]
  4. 返回结果

    • 循环结束后,result 即为第 a 项的斐波那契数。

复杂度分析:

  • 时间复杂度:O(A),因为需要进行 A - 2 次迭代。
  • 空间复杂度:O(1),因为只使用了常数个额外的变量来存储数组和中间结果。