Python 练习讲解 · 17 / 25

枚举与乘法原理:组成三位偶数

让程序穷举验证,再用分类计数解释为什么答案是这个数。

要学习的知识点

题目

练习 17:枚举 0~9 中不重复数字组成的三位偶数,并用乘法原理验证。

参考程序:ex17.py;空白练习:lx17.py。

完整代码

以下内容与 ex17.py 一致,可对照右侧讲解阅读。

"""练习 17:枚举 0~9 中不重复数字组成的三位偶数,并用乘法原理验证。

学习说明:
- 三重循环分别选择百位、十位、个位;百位从 1 开始,保证数是三位数。
- 偶数的个位只能是 0、2、4、6、8;三个位置还必须互不相同。
- 乘法原理计算时分个位为 0 和个位非 0 两种情况,避免把可选数字数错。
- 两种独立方法都求出数量,最后输出以便核对。
"""
count_by_enumeration = 0
# 逐一枚举三个数位的所有候选组合。
for hundreds in range(1, 10):
    for tens in range(10):
        for ones in (0, 2, 4, 6, 8):
            if hundreds != tens and hundreds != ones and tens != ones:
                count_by_enumeration += 1

# 个位为 0:9 种百位 × 8 种十位。
# 个位为 2、4、6、8:4 × 8 种百位 × 8 种十位。
count_by_rule = 9 * 8 + 4 * 8 * 8
print("enumeration:", count_by_enumeration)
print("counting principle:", count_by_rule)

进阶写法

这版与初学版解决同一道题,使用更精炼的语法或标准库工具。

进阶源码:ex17_adv.py

"""练习 17 进阶版:用 itertools.permutations 枚举不重复数位。"""
from itertools import permutations

digits = "0123456789"
valid_numbers = (
    number for number in permutations(digits, 3)
    if number[0] != "0" and number[2] in "02468"
)
enumeration_count = sum(1 for _ in valid_numbers)
counting_principle_count = 9 * 8 + 4 * 8 * 8
print("enumeration:", enumeration_count)
print("counting principle:", counting_principle_count)

permutations() 生成不重复数位排列;过滤百位 0 和奇数个位后用 sum() 计数。公式部分仍按个位是否为 0 分类。

运行结果

示例输入:

(本题程序不需要输入)

程序输出:

enumeration: 328
counting principle: 328

逐个认识函数

下表根据程序中实际调用的函数和方法整理;自定义函数的实现与算法细节见后续说明。

函数形式功能与返回值注意事项
print(*objects, sep=' ', end='\n')把对象转换为文本显示;返回 None。默认空格分隔并在末尾换行。
range(start, stop, step=1)产生整数序列供循环或索引使用。stop 不包含在序列里。

先把情况分开

个位为偶数,有两类情况:

  1. 个位是 0:百位可选 1–9,共 9 种;十位不能与百位重复,也不能为 0(因为 0 已在个位),剩 8 种。合计 9×8=72。
  2. 个位是 2、4、6、8:个位有 4 种。固定个位后,百位可从 1–9 中选,排除个位,剩 8 种;十位从 0–9 中选,排除已用的百位和个位,剩 8 种。合计 4×8×8=256。
  3. 两类互斥,合计 72+256=328。

这是加法原理和乘法原理的配合:同一类的连续步骤相乘,不同且互斥的类别相加。

程序中的语法与内置函数

写法参数 / 返回值作用
range(start, stop)生成 start 到 stop-1 的整数序列。hundreds 从 1 到 9,确保三位数;tens 从 0 到 9。
(0, 2, 4, 6, 8)元组字面量,可直接遍历。限定 ones 只能取偶数个位。
and逻辑运算符,不是函数;两边条件都真时结果为 True。检查三个数位两两不同。
+=累加赋值,等价于左边变量加右边值再保存。每找到一种组合,将总数加 1。
print(...)输出对象,返回 None。同时展示枚举结果和公式结果用于交叉验证。

枚举为何可信

三层循环遍历百位、十位、个位所有合法范围;条件确保无重复。每种满足条件的数恰好经过一次,因此统计值准确。公式计数则把可能性按个位分类,避免逐个列出。两种方法结果相同,是很好的检查方式。

数位的“位置”很重要:数字 0 可以出现在十位或个位,但不能出现在百位。计数时不能把每个位置都简单当成 10 种。

代码如何运行

  1. 先按左侧题目给出的格式输入数据;程序通过 input() 读取,并在需要时转换数据类型。
  2. 程序自上而下执行语句;遇到条件、循环或函数调用时,按照右侧算法说明更新变量和处理数据。
  3. 最后由 print() 输出计算结果。可将左侧示例输入与输出逐项对照。

先自己跟踪一组小数据,记录循环前后变量的值;这比只看最终答案更容易理解程序。

自己动手试一试

将三位偶数改为三位奇数,重新分类个位情况,再用枚举结果验证公式。

先在 lx17.py 中独立完成,再运行参考程序比较结果。修改程序时一次只改一个条件或步骤。