Python递推经典问题(斐波那契数列)
困难2从兔子生小兔学会递推:斐波那契数列的超简单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
代码解释:
a和b就像两个移动的“标签”,始终指向最近的两个数。初始时a=1(第1项),b=1(第2项)。- 每次循环,我们把
b变成新的a,把a+b变成新的b。
比如第1次循环(i=3):a, b = 1, 1+1=2→a=1, b=2(此时a代表第2项,b代表第3项)
第2次循环(i=4):a, b = 2, 1+2=3→a=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, b 或 prev, 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万了!兔子要是真这么生,农场很快就要挤爆啦 ?
小挑战:自己动手试一试
- 用上面的函数,算算 第40项 是多少?会不会超过一千万?
- 修改代码,让用户输入一个数字
n,然后输出第n项。提示:用input()函数。 - 如果兔子不是从第2个月开始生,而是从第3个月才开始生(即第1、2、3个月都是1对,第4个月变成2对,第5个月3对……),这个数列会变成什么样?试着修改代码看看。
相关知识点指引
学会斐波那契数列的递推,你还想了解什么?
- 递推与递归的区别:递归是“从后往前调用自己”,递推是“从前往后循环计算”。斐波那契用递归写更直观,但效率低(重复计算太多),用递推循环更快。你可以搜索“斐波那契数列递归 vs 递推”来对比。
- 动态规划:递推是动态规划的最简单形式。以后你学“爬楼梯问题”“硬币找零”等,本质都是递推。
- 其他递推经典问题:
- 汉诺塔:移动盘子,规律是
H(n) = 2*H(n-1) + 1 - 杨辉三角:每个数等于它上面两个数之和
- 铺砖问题:用1×2砖铺满2×n的地面,有多少种铺法?其实就是斐波那契数列!
- 汉诺塔:移动盘子,规律是
递推的思想就像“由已知推未知”,在编程中非常常用。掌握了它,你就能解决很多“一步步计算”的问题。继续加油吧!
例题精讲
已知斐波那契数列定义为F(0)=0, F(1)=1,后续项为前两项之和。那么F(7)的值是多少?
使用递推(迭代)方法计算斐波那契数列第n项,其时间复杂度是?
用递归方法计算斐波那契数列第n项的时间复杂度是O(n)。
斐波那契数列的递推公式F(n)=F(n-1)+F(n-2)对于任意整数n≥2都成立(假设已定义F(0)和F(1))。
下面是用递推法计算斐波那契数列第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