教程建议 35 分钟学习等级 8/8

PYTHON LESSON 114

杨辉三角的奥秘

掌握杨辉三角的递推规律,会用二维列表生成任意行,并理解“第 n 行第 m 个数 = C(n, m)”。

00 · 学习目标

这一课要解决什么?

先想一想一个只有 1 的三角形,怎么“长”出所有组合数?
完成任务杨辉三角打印机
学习顺序定义 → 语法 → 最小实例 → 独立练习

学完后,你应该能够

  • row = [1] * (i + 1) 快速造出全 1 的行
  • row[j] = 上一行[j-1] + 上一行[j] 递推
  • 列表嵌套 triangle[i][j] 表示第 i 行第 j 个数

01 · 核心概念

杨辉三角的递推构造与组合数恒等式

杨辉三角的递推构造与组合数恒等式:杨辉三角每个数都等于它“肩上”两个数之和,边缘全是 1。这个规律写成式子就是组合数恒等式 C(n, m) = C(n−1, m−1) + C(n−1, m),所以第 n 行第 m 个数正好是 C(n, m)。

考级对应GESP Python 8 级 · 杨辉三角GESP Python 8 级 · 排列与组合(编程求解)
学习边界

概率需要在计数结果上再建立样本空间,不等同于组合数本身。

02 · 语法与规则

先记住这 3 条,再开始写程序

01row = [1] * (i + 1) 快速造出全 1 的行

先准确读出这条写法的结构与作用。

02row[j] = 上一行[j-1] + 上一行[j] 递推

换一组最小数据,手工推演一次结果。

03列表嵌套 triangle[i][j] 表示第 i 行第 j 个数

再用边界值或反例确认它的适用条件。

03 · 完整实例

代码、运行结果和解释放在一起看

pascal-triangle.pyPYTHON 3.12
# 杨辉三角:每一行都是组合数的真身
n = 6
triangle = []
for i in range(n):
    row = [1] * (i + 1)               # 每行首尾都是 1
    for j in range(1, i):
        row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]  # 肩上两数之和
    triangle.append(row)
    print(" ".join(map(str, row)))

# 第 5 行(从 0 数)第 2 个数,就是组合数 C(5, 2)
print("C(5, 2) =", triangle[5][2])
运行结果OUTPUT
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
C(5, 2) = 10

行号与列号都从 0 开始数:第 i 行第 j 个数恰好是 C(i, j)。

在新标签运行和修改这个实例已装入本课代码 · 可自定义输入 · 可提交判题

04 · 逐步理解

每一步只解决一个问题

  1. 01

    看懂递推规律

    杨辉三角每个数都等于它“肩上”两个数之和,边缘全是 1。这个规律写成式子就是组合数恒等式 C(n, m) = C(n−1, m−1) + C(n−1, m),所以第 n 行第 m 个数正好是 C(n, m)。

    PYTHON
    row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]
  2. 02

    用 [1] * (i+1) 造好一行

    第 i 行有 i+1 个数,且首尾必为 1。先用 [1] * (i + 1) 造出一行全 1 的列表,再只更新中间位置(j 从 1 到 i−1),边缘自动正确,省心又不易错。

    PYTHON
    row = [1] * (i + 1)
    for j in range(1, i):
        row[j] = triangle[i-1][j-1] + triangle[i-1][j]
  3. 03

    一行一行存进大列表

    用列表 triangle 把每一行追加进去,triangle[i] 就是第 i 行。这是“二维列表”的典型用法:triangle[i][j] 先行后列,像查表格一样取数。

    PYTHON
    triangle.append(row)
  4. 04

    把整行漂亮地输出

    " ".join(map(str, row)) 是竞赛常用套路:map(str, row) 把每个数转成字符串,join 用空格缝成一行,行尾不会有多余空格——OJ 判题对格式很挑剔。

    PYTHON
    print(" ".join(map(str, row)))
  5. 05

    验证“第 i 行第 j 个 = C(i, j)”

    打印完 6 行后取 triangle[5][2],结果是 10,与 comb(5, 2) 完全一致。以后题目要求组合数而 n 又不太大时,杨辉三角递推比阶乘更稳、更快。

    PYTHON
    print(triangle[5][2])  # 10
  6. 06

    复杂度小算盘

    生成 n 行杨辉三角要填 1+2+…+n ≈ n²÷2 个数,时间复杂度 O(n²),空间 O(n²)。若只问某一行,其实只保留上一行就够,空间可压到 O(n)——这就是“滚动数组”思想的萌芽。

05 · 练习与检验

自己写出来,才算真正学会

本课实作步骤
  1. 1运行打印机,观察前 6 行规律
  2. 2验证 C(5, 2) 与第 5 行第 2 个数相等
  3. 3把行数改成 10 再看输出
  4. 4用组合数解释为什么每行首尾都是 1
打开本课编程实验室编辑、运行、测试、判题都在一个页面完成

06 · 完整知识

继续理解定义、规则和适用边界

第一次学习先完成上面的六个步骤;需要查定义、核对规则、分析误区或理解“为什么”时,再展开对应知识章。

数学基础计数原理、组合数与计算几何基础明确有序/无序、互斥/分步与几何退化边界,再把公式转为稳定程序。

正式定义

加法原理用于互斥方案分类求和,乘法原理用于依次完成步骤求积;排列关心顺序,组合不关心顺序。计算几何用坐标、向量、距离和方向关系把图形条件转成数值判定。

必须掌握

  • A(n,m)=n!/(n-m)!,C(n,m)=n!/(m!(n-m)!),并有 C(n,m)=C(n,n-m)。
  • 杨辉三角递推 C(n,m)=C(n-1,m-1)+C(n-1,m),边界 C(n,0)=C(n,n)=1。
  • 取模下的组合数算法取决于 n、模数是否为质数、查询次数和是否允许预处理。
  • 距离比较应优先比较平方,避免不必要的 sqrt 和浮点误差。
  • 三角形必须满足任意两边之和大于第三边;共线、重合、零长度是几何边界。
  • 浮点比较应按题目误差使用容差;整数坐标能用叉积时优先保持整数计算。

常见误区

  • 未判断顺序是否重要就套排列组合公式
  • 模意义下直接做普通除法
  • 浮点数直接用 ==
  • 漏掉共线或退化图形

适用边界

  • 概率需要在计数结果上再建立样本空间,不等同于组合数本身。
  • 复杂计算几何涉及方向、相交、凸包等更多主题,本章只覆盖课程实际使用的基础判定。
打开本章完整示例与独立阅读页 →
编码与文本字符串、转义、切片与格式化从不可变字符序列到检索、拆分、拼接、格式化和常用判断方法。

正式定义

str 是不可变的 Unicode 字符序列。下标访问单个字符,切片生成新字符串;任何看似“修改字符串”的方法都会返回新对象。

必须掌握

  • 下标从 0 开始,负下标从末尾开始;切片 s[start:stop:step] 不包含 stop,step 不能为 0。
  • 转义序列用于在字面量中表示换行、制表、引号、反斜杠或码点;原始字符串仍有末尾反斜杠限制。
  • find 找不到返回 -1,index 找不到抛 ValueError;count 统计不重叠出现次数。
  • split 把字符串拆成列表,join 用一个字符串连接可迭代对象中的字符串,strip 只删除两端字符。
  • f-string 的格式说明可控制宽度、对齐、精度、进制和百分比;格式化不改变原值。
  • isalpha/isdigit 等按 Unicode 定义,不只识别英文字母和 ASCII 数字。

常见误区

  • 尝试 s[0] = 'A' 原地修改字符串
  • 把 strip('ab') 误解为删除完整子串 'ab'
  • find 返回 -1 后直接拿去当有效下标
  • 把字节长度与字符长度混为一谈

适用边界

  • 正则表达式不在低等级字符串必修范围,但复杂模式匹配时应使用 re,而不是堆叠大量 split/find。
  • 面向用户的字符计数还可能涉及组合字符和字素簇,len 统计的是 Unicode 码点序列长度。
打开本章完整示例与独立阅读页 →
程序组织模块、导入与常用标准库正确导入模块,并按用途查找 math、random、statistics、collections、heapq、bisect 等标准工具。

正式定义

模块是可导入的 Python 代码单元,包把模块组织成层次。import 先加载模块并创建模块对象,再把名字绑定到当前命名空间;标准库随 Python 分发,不等于第三方包。

必须掌握

  • import module 保留清晰命名空间;from module import name 只导入指定名字;避免 from module import *。
  • math 提供 sqrt、floor、ceil、gcd、log、sin 等数学函数和 pi、e 等常量。
  • random 用于伪随机模拟,不适合密码安全;需要安全随机时使用 secrets。
  • collections.deque 适合双端队列,Counter 适合计数,defaultdict 可按工厂创建缺省值。
  • heapq 实现最小堆,bisect 在已排序序列中二分定位,itertools 提供高效迭代组合工具。
  • if __name__ == '__main__': 用来区分文件被直接运行还是被导入。

常见误区

  • 文件名写成 math.py 导致遮蔽标准库
  • import * 污染命名空间
  • 把 random 当成加密随机数
  • 没有理解工具的数据复杂度就盲目调用

适用边界

  • 标准库非常大,本章覆盖课程会实际依赖的入口;具体函数签名应结合本站条目与运行时帮助查询。
  • Turtle 需要图形窗口,本站文本判题环境不伪造其画布。
打开本章完整示例与独立阅读页 →
工程能力异常、文件、测试与调试读懂报错、缩小问题、设计测试,并安全地打开、读取和关闭文本文件。

正式定义

异常是在运行期间表示错误或特殊情况的对象。调试是用可复现输入和证据定位实际行为与预期行为差异的过程;文件对象连接程序与持久化字节数据。

必须掌握

  • 先读 traceback 最后一行的异常类型与消息,再从最靠近自己代码的栈帧向上追踪。
  • try 只包可能失败的最小代码;except 捕获具体异常;else 处理成功路径;finally 做必需清理。
  • raise 主动报告不满足的前置条件;assert 用于开发期内部假设,不用于校验不可信用户输入。
  • with open(...) as file 会在退出代码块时可靠关闭文件。文本模式必须明确编码,本站统一推荐 encoding='utf-8'。
  • 测试至少包含正常值、边界值、空数据、极端值和反例;每个测试只应有明确目的。
  • 定位错误时一次只改一个假设,保留能稳定复现问题的最小输入。

常见误区

  • 使用 except: 吞掉所有错误
  • 只测题目样例就认为程序正确
  • 文本文件不写 encoding
  • 修复报错表象却不验证根因

适用边界

  • 在线判题的学生代码由独立 Worker 执行;文件系统、网络和资源权限必须受平台限制。
  • 二进制文件、JSON/CSV 和数据库各有专门格式与错误处理方式,不能按普通文本随意拆分。
打开本章完整示例与独立阅读页 →

完成检查

确认自己会解释、会编写、会验证