Python枚举算法
中等3枚举算法:把所有可能都试一遍
想象一下,你有一把密码锁,密码是三位数字(000~999),但你不记得了。最笨但最管用的办法是什么?从000开始,一个个试到999。这就是枚举——把所有可能的情况都列举出来,逐一检查,直到找到答案。
在编程里,枚举也叫暴力破解,是一种非常直接的方法。它不讲究什么巧妙的数学公式,就是靠计算机跑得快,把所有情况都测试一遍。
什么时候用枚举?
生活中的例子
- 猜数字游戏:对方心里想了一个1~100之间的数,你从1开始每个都猜一遍。虽然慢,但一定猜对。
- 找零钱:你有1元、2元、5元三种硬币,要凑出10元钱。枚举所有可能的组合(比如1元0个,2元0个,5元2个……),看看哪些方案正好凑出10元。
- 考试猜答案:10道判断题,每道题可能对或错。把所有2¹⁰=1024种答案组合都试一遍,总有一份是全对的(前提是你有标准答案)。
典型场景
- 范围比较小(比如小于1000种可能)时,枚举简单又可靠。
- 你想不出更好的算法时,先用枚举保底。
- 作为验证其他算法是否正确的“标准答案”。
Python里怎么写枚举?
最常用的工具是for循环。我们用一个例子来看:找出1~50之间所有既能被3整除又能被5整除的数(也就是15的倍数)。
# 枚举1到50的整数,找出同时是3和5倍数的数
for number in range(1, 51): # number: 当前检查的数字
if number % 3 == 0 and number % 5 == 0: # 同时满足两个条件
print(number, "是3和5的倍数")
运行结果会输出:15、30、45。这就是枚举:把每个数都检查一遍,符合条件就记下来。
枚举的通用步骤
- 确定范围:比如从1到100,或者从a到b的所有整数。
- 循环遍历:用
for循环依次取出范围内的每个值。 - 条件判断:用
if判断当前值是否符合题目要求。 - 记录结果:如果符合,输出或保存起来。
枚举的优缺点
| 优点 | 缺点 |
|---|---|
| 思路简单,容易写 | 当可能情况很多时,会非常慢 |
| 保证能找到答案(只要范围完整) | 需要消耗大量计算资源 |
| 适合新手理解“遍历所有可能” | 不适合大数据或高维问题 |
比如枚举一个8位数的密码(每位0~9),有10⁸=1亿种可能,普通电脑可能需要几秒钟甚至更久。如果密码是10位,那就是100亿次,现实世界中等不了。
常见错误(新手最容易踩的坑)
1. 范围写错,少了边界
# 错误:想找1~100,却只到99
for i in range(1, 100): # range(1,100)不包括100
修正:用range(1, 101)或者range(1, 101, 1)。
2. 枚举范围不完整,漏掉答案
比如找“100以内质数”时,只检查到int(num**0.5),虽然是对的,但如果忘了加1(比如写成int(num**0.5)而不是int(num**0.5)+1),就可能漏掉完全平方数的情况。例如检查4时,int(4**0.5)=2,如果循环是range(2,2)则根本不会执行,以为4是质数,但实际上4不是质数(能被2整除)。
3. 枚举顺序混乱或重复
比如要找出三个数的所有组合,如果循环写错,可能重复计算相同的组合。例如从{1,2,3}中选两个不同的数,如果写成:
for x in [1,2,3]:
for y in [1,2,3]:
if x != y:
print(x, y)
会输出(1,2)(1,3)(2,1)(2,3)(3,1)(3,2),虽然没重复数,但(1,2)和(2,1)被视为不同,有时我们只想要组合(不考虑顺序)。这时候就要调整循环范围防止重复:例如让第二个循环从第一个的后一位开始。
4. 忘记重置状态
在循环里检查某个条件时,有时候需要先假设“真”,然后在循环内部发现不符合就改成“假”。如果忘记每次循环开始时重置,上一次的结果会影响下一次。下面找质数的例子就需要注意这一点。
完整示例:找水仙花数
水仙花数:一个三位数,每个数位上的数字的立方和等于它本身。比如153 = 1³ + 5³ + 3³。
我们来枚举所有三位数(100~999),逐个检查。
# 找出所有水仙花数(三位数)
print("水仙花数有:")
for num in range(100, 1000): # num: 当前检查的三位数
a = num // 100 # a: 百位数字
b = (num // 10) % 10 # b: 十位数字
c = num % 10 # c: 个位数字
if a**3 + b**3 + c**3 == num: # 立方和等于本身?
print(num, " = ", a, "^3+", b, "^3+", c, "^3")
运行结果:
水仙花数有:
153 = 1 ^3+ 5 ^3+ 3 ^3
370 = 3 ^3+ 7 ^3+ 0 ^3
371 = 3 ^3+ 7 ^3+ 1 ^3
407 = 4 ^3+ 0 ^3+ 7 ^3
这个例子完整展示了枚举的四步:范围(100~999)、循环、判断条件、输出。
更多练习:百钱百鸡问题
我国古代数学题:公鸡5文一只,母鸡3文一只,小鸡1文三只。用100文钱买100只鸡,问公鸡、母鸡、小鸡各多少只?
我们可以枚举公鸡数量(最多20只),母鸡数量(最多33只),小鸡数量由总数减去前两种得到,但要检查钱数是否匹配。
# 百钱百鸡问题:枚举所有可能的公鸡数和母鸡数
for rooster in range(0, 21): # rooster: 公鸡数量 (0~20)
for hen in range(0, 34): # hen: 母鸡数量 (0~33)
chick = 100 - rooster - hen # chick: 小鸡数量,总数为100
if chick % 3 != 0: # 小鸡数量必须是3的倍数,因为3只1文
continue
total_money = rooster * 5 + hen * 3 + chick // 3
if total_money == 100:
print(f"公鸡{rooster}只,母鸡{hen}只,小鸡{chick}只")
这里用了两层循环枚举公鸡和母鸡,小鸡自动算出,再检查条件。同样是枚举思想。
学完枚举后可以学什么?
- 剪枝:在枚举过程中提前排除不可能的情况,减少尝试次数(比如百钱百鸡里公鸡数不可能超过20,母鸡不可能超过33)。
- 回溯:一种更高级的枚举方式,常用于走迷宫、八皇后等问题。
- 二分查找:当数据有序时,不用枚举,更快。
- 循环嵌套与列表:很多时候枚举的结果需要存起来,可以用列表存储。
枚举是编程的“基本功”,很多复杂问题的解法里都藏着枚举的影子。当你遇到问题没思路时,不妨先写一个枚举程序,看看结果是什么样的,然后再想办法优化。记住:枚举虽慢,但很可靠。
例题精讲
以下哪个问题最适合使用枚举算法解决?
枚举算法的优点是总能找到问题的正确答案,但可能因枚举范围过大而导致执行效率较低。
以下程序使用枚举算法找出1到100之间所有既能被3整除又能被5整除的数,请填写正确的条件。
for i in range(1, 101):
if ___ :
print(i)关于枚举算法的描述,下列哪一项是正确的?
使用枚举算法求解“鸡兔同笼”问题时,只需枚举鸡的数量即可求出所有可能的解。