CC++ & Algorithm

Python递推经典问题(斐波那契数列)

困难2
语言版本:C++Python
概述:用兔子生小兔的故事,学会用递推方法解决斐波那契数列问题。

从兔子生小兔学会递推:斐波那契数列的超简单Python实现

假如你有一个神奇的兔子农场:一开始只有一对刚出生的兔子(一公一母)。这对兔子要长两个月才能生宝宝,之后每个月都能生一对新兔子(也是公母各一),而且兔子永远不会死。那么,每个月农场里总共有多少对兔子呢?

这个有趣的问题背后,藏着一个超级有名的数列——斐波那契数列。学会用Python递推解决它,你以后遇到很多“由前推后”的问题都能轻松搞定!


什么是斐波那契数列?——兔子生小兔的故事

我们来模拟一下兔子数量的变化:

  • 第1个月:只有最初那对兔子(还没长大),总共 1对
  • 第2个月:它们长大了,但还没生,总共还是 1对
  • 第3个月:这对大兔生下一对小兔,现在有:1对大兔 + 1对小兔 = 2对
  • 第4个月:大兔又生了一对,而之前的小兔刚长大还没生,现在有:1对大兔 + 1对刚长大但没生的兔子 + 1对新小兔 = 3对
  • 第5个月:上个月的两对大兔各生一对(因为它们都是“成熟”的),加上之前的一对小兔也长大但没生,共 5对
  • 继续下去:8对、13对、21对……

把每月的兔子对数写出来:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55……

你发现规律了吗?
从第3个数开始,每个数都等于它前面两个数的和。比如:
2 = 1 + 1
3 = 1 + 2
5 = 2 + 3
8 = 3 + 5
13 = 5 + 8

这个数列就叫 斐波那契数列,最早就是用来描述兔子繁殖的。数学家斐波那契在几百年前把它写了出来,所以用他的名字命名。


递推思想:像搭积木一样一步步推

“递推”就是从已知的条件出发,一步一步推出后面的结果。就像你玩搭积木:先放好最下面两块,然后每一块都放在它下面两块的正中间,这样一层层搭上去。

对于斐波那契数列,我们已知:

  • 第1项 = 1
  • 第2项 = 1

然后从第3项开始,每一项都是前两项的和:

  • 第3项 = 第1项 + 第2项 = 1 + 1 = 2
  • 第4项 = 第2项 + 第3项 = 1 + 2 = 3
  • 第5项 = 第3项 + 第4项 = 2 + 3 = 5
  • ……
  • 第n项 = 第(n-2)项 + 第(n-1)项

关键:我们不需要记住前面所有项,只需要记住最近的两项,就能推出下一项。就像你走路时,只需要知道当前脚的位置和下一步要迈的方向,不需要记住以前所有脚印。


Python代码实现:用循环一步一步递推

用循环写斐波那契数列非常简单。我们定义一个函数 fibonacci(n),返回第n项的值。

基础版:只算第n项

def fibonacci(n):
    # 如果n<=0,返回0(一般不会有这种情况,但做好防护)
    if n <= 0:
        return 0
    # 第1项和第2项都是1
    elif n == 1 or n == 2:
        return 1
    
    a, b = 1, 1           # a是第1项,b是第2项,我们总是保存最近两项
    for i in range(3, n+1):   # 从第3项一直算到第n项
        a, b = b, a + b       # 递推:新的a变成旧的b,新的b变成旧的a+旧的b
    return b                # 循环结束时,b就是第n项

# 测试:输出前10项
for i in range(1, 11):
    print(f"第{i}项: {fibonacci(i)}")

运行结果

第1项: 1
第2项: 1
第3项: 2
第4项: 3
第5项: 5
第6项: 8
第7项: 13
第8项: 21
第9项: 34
第10项: 55

代码解释

  • ab 就像两个移动的“标签”,始终指向最近的两个数。初始时 a=1(第1项),b=1(第2项)。
  • 每次循环,我们把 b 变成新的 a,把 a+b 变成新的 b
    比如第1次循环(i=3):a, b = 1, 1+1=2a=1, b=2(此时a代表第2项,b代表第3项)
    第2次循环(i=4):a, b = 2, 1+2=3a=2, b=3(a代表第3项,b代表第4项)
    这样一直推下去,最后b就是第n项。

生活比喻:想象你在数台阶,每迈一步,你只需要记住你刚踩到的那一级你正在踩的那一级,就能算出下一步该踩哪一级。递推就这这么省力。


常见错误(新手最容易踩的坑)

错误1:忘记处理前两项的特殊情况

def fibonacci_wrong(n):
    a, b = 1, 1
    for i in range(3, n+1):   # 如果n=1或n=2,这个循环根本不会运行
        a, b = b, a + b
    return b

如果调用 fibonacci_wrong(1),循环不执行,直接返回 b(初始值是1),虽然结果没错,但如果n=0或负数呢?代码没做判断,会返回1(实际应为0)。所以一定要加 if 分支。

错误2:循环范围写错

for i in range(3, n):   # 漏掉了第n项

写成 range(3, n) 会少算一次,比如n=5,只循环到i=4,只得到第4项,不是第5项。正确写法是 range(3, n+1)

错误3:返回了a而不是b

return a   # 此时a是第n-1项,不是第n项

循环结束后,a 是上一次的 b(也就是第n-1项),b 才是第n项。记住:最后更新的是 b,所以返回 b

错误4:变量名起得太长或没写注释

number1 = 1
number2 = 1
for i in range(...):
    number1, number2 = number2, number1 + number2

虽然也正确,但名字太啰嗦,建议用 a, bprev, curr,并加上中文注释。


完整可运行的示例:输出前20项并计算第30项

下面给出一个完整程序,包含函数定义、前20项输出、以及第30项的计算。

def fibonacci(n):
    """
    返回斐波那契数列的第 n 项(n 从1开始)
    """
    if n <= 0:
        return 0
    elif n == 1 or n == 2:
        return 1
    
    a, b = 1, 1          # a: 第1项, b: 第2项
    for i in range(3, n+1):   # 从第3项算到第n项
        a, b = b, a + b       # 递推:新a = 旧b, 新b = 旧a+旧b
    return b

# 主程序
print("斐波那契数列前20项:")
for i in range(1, 21):
    print(f"第{i:2d}项 = {fibonacci(i):5d}")   # :2d 和 :5d 是对齐格式,让输出好看

print("\n计算第30项……")
result_30 = fibonacci(30)
print(f"第30项 = {result_30}")

运行结果

斐波那契数列前20项:
第 1项 =     1
第 2项 =     1
第 3项 =     2
第 4项 =     3
第 5项 =     5
第 6项 =     8
第 7项 =    13
第 8项 =    21
第 9项 =    34
第10项 =    55
第11项 =    89
第12项 =   144
第13项 =   233
第14项 =   377
第15项 =   610
第16项 =   987
第17项 =  1597
第18项 =  2584
第19项 =  4181
第20项 =  6765

计算第30项……
第30项 = 832040

哇,第30项已经超过80万了!兔子要是真这么生,农场很快就要挤爆啦 ?


小挑战:自己动手试一试

  1. 用上面的函数,算算 第40项 是多少?会不会超过一千万?
  2. 修改代码,让用户输入一个数字 n,然后输出第 n 项。提示:用 input() 函数。
  3. 如果兔子不是从第2个月开始生,而是从第3个月才开始生(即第1、2、3个月都是1对,第4个月变成2对,第5个月3对……),这个数列会变成什么样?试着修改代码看看。

相关知识点指引

学会斐波那契数列的递推,你还想了解什么?

  • 递推与递归的区别:递归是“从后往前调用自己”,递推是“从前往后循环计算”。斐波那契用递归写更直观,但效率低(重复计算太多),用递推循环更快。你可以搜索“斐波那契数列递归 vs 递推”来对比。
  • 动态规划:递推是动态规划的最简单形式。以后你学“爬楼梯问题”“硬币找零”等,本质都是递推。
  • 其他递推经典问题
    • 汉诺塔:移动盘子,规律是 H(n) = 2*H(n-1) + 1
    • 杨辉三角:每个数等于它上面两个数之和
    • 铺砖问题:用1×2砖铺满2×n的地面,有多少种铺法?其实就是斐波那契数列!

递推的思想就像“由已知推未知”,在编程中非常常用。掌握了它,你就能解决很多“一步步计算”的问题。继续加油吧!

例题精讲

1单选题

已知斐波那契数列定义为F(0)=0, F(1)=1,后续项为前两项之和。那么F(7)的值是多少?

A8
B13
C21
D34
2单选题

使用递推(迭代)方法计算斐波那契数列第n项,其时间复杂度是?

AO(1)
BO(n)
CO(2^n)
DO(n^2)
3判断题

用递归方法计算斐波那契数列第n项的时间复杂度是O(n)。

4判断题

斐波那契数列的递推公式F(n)=F(n-1)+F(n-2)对于任意整数n≥2都成立(假设已定义F(0)和F(1))。

5填空题
下面是用递推法计算斐波那契数列第n项的Python函数,请补全代码。\ndef fib(n):\n    if n <= 1:\n        return n\n    a, b = 0, 1\n    for i in range(2, n+1):\n        a, b = b, ___\n    return b