
离散数学学习资料
离散数学学习资料
面向期末考试复习与自学速成,以认知规律编排内容,强调直觉理解。
每章从一个真实问题出发,让理论成为解决问题的自然产物。
目录
- 集合论
- 命题逻辑
- 一阶逻辑
- 二元关系和函数
- 图
- 特殊图
- 树
- 组合分析初步
- 代数系统简介
第1章 集合论
本章定位:建立全书的语言基础——集合是离散数学的"普通话"。本章不假设你有任何离散数学的前置知识,一切从零开始。
驱动问题
19世纪末,数学家康托尔遇到了一个令人不安的问题:无穷大之间有大小之分吗?
自然数有无穷多个,实数也有无穷多个。直觉上它们"都是无穷",应该一样多?但康托尔证明了:实数比自然数"多得多"——无穷有不同的层次。
为了精确讨论这类问题,他发明了集合论。集合论后来成了整个现代数学的地基——几乎所有数学对象都可以用集合来定义。
你已经在用集合了:搜索引擎的筛选条件(年龄>18 且 城市=北京)就是集合的交运算;数据库的 SELECT ... WHERE 条件1 AND 条件2 底层就是集合运算;你整理文件时"把PDF和Word都归到文档文件夹",就是在做并集。
1.1 集合的基本概念
为什么需要集合?
在日常语言中,我们经常需要描述"一群东西":这学期选的课、所有偶数、班里的女生……但自然语言有歧义——"所有偶数"是包含负偶数吗?“班里的女生"包括转学的吗?我们需要一个精确的数学语言来描述"一群东西”。
概念讲解
集合(Set):把一些确定的、可区分的对象放在一起形成的整体。
- “确定的”:对于任何一个对象,它要么在集合里,要么不在,没有模糊地带
- “可区分的”:集合中的每个对象都是可以辨认的个体
直觉类比:集合就像一个袋子,里面装着东西。袋子有三个特点:(1) 不在乎顺序——{1,2,3} 和 {3,1,2} 是同一个集合;(2) 不重复计数——{1,1,2} 其实就是 {1,2};(3) 只关心"在不在",不关心"有多少个"。
元素(Element):集合中的每个对象。如果 a 是集合 A 中的一个对象,我们说"a 属于 A",记作:
1 | a ∈ A |
符号 ∈ 读作"属于"。它的反面是 ∉,读作"不属于"。例如 3 ∈ {1, 2, 3},但 4 ∉ {1, 2, 3}。
空集(Empty Set):没有任何元素的集合,记作 ∅ 或 {}。
你可能会想:空集有什么用?它是数学中的"零"——就像 0 是一个数一样,∅ 是一个集合。它是后续很多定义和证明的起点。
全集(Universal Set):当前讨论范围内所有元素构成的集合,记作 U。
全集不是固定的,取决于你在讨论什么。如果讨论"整数的性质",U 就是所有整数;如果讨论"班里的同学",U 就是班里所有人。每次讨论都要先明确 U 是什么。
集合的表示方法
列举法:把元素一一列出,用花括号 {} 括起来。
1 | A = {1, 2, 3, 4, 5} ← 包含 1 到 5 这五个自然数 |
描述法(也叫特征性质法):用一个条件来定义集合。写法是 {x | 条件},读作"满足条件的 x 的集合"。
1 | A = {x | x 是正整数且 x ≤ 5} ← 和上面的列举法 {1,2,3,4,5} 等价 |
竖线 | 读作"使得"或"满足"。{x | x 是正整数且 x ≤ 5} 读作"x 的集合,使得 x 是正整数且 x ≤ 5"。
子集与真子集
子集(Subset):如果集合 A 中的每个元素都在集合 B 中,就说"A 是 B 的子集",记作 A ⊆ B。
直觉类比:A 是 B 的"一部分"。如果 B 是你衣柜里的所有衣服,A 是衣柜里的所有衬衫,那么 A ⊆ B。
用符号写:A ⊆ B 的意思是"对任意 x,如果 x ∈ A,那么 x ∈ B"。注意"任意"这个词——必须是每一个元素都在 B 中才行,漏掉一个都不算。
例子:
1 | {1, 2} ⊆ {1, 2, 3} ← 1 和 2 都在 {1,2,3} 中 ✓ |
真子集(Proper Subset):A ⊆ B 且 A ≠ B,记作 A ⊂ B。意思是"A 确实是 B 的一部分,不是全部"。
区分:⊆ 允许 A = B(自己是自己的子集),⊂ 不允许(必须是真的一部分)。
重要性质:空集是任何集合的子集。即 ∅ ⊆ A 对任意集合 A 成立。
为什么?子集的定义是"对任意 x,如果 x ∈ ∅,那么 x ∈ A"。但 ∅ 里根本没有元素,所以"x ∈ ∅"这个前提永远不会成立。在逻辑中,当前提不成立时,整个"如果…那么…"的命题自动为真。这叫空虚真(vacuously true)。
类比:我说"如果太阳从西边出来,我就请你吃饭"——太阳不会从西边出来,所以这个承诺永远不会被检验,你不能说我食言了。这就是空虚真。
约定与记号
| 记号 | 含义 | 读法 |
|---|---|---|
| ∈ | 属于 | “a 属于 A” |
| ∉ | 不属于 | “a 不属于 A” |
| ⊆ | 子集 | “A 包含于 B” 或 “B 包含 A” |
| ⊂ | 真子集 | “A 真包含于 B” |
| ∅ | 空集 | “空集” |
| U | 全集 | “全集” |
| |A| | 集合 A 的元素个数 | “A 的基数” 或 “A 的大小” |
|A| 叫做集合的基数(Cardinality)。对于有限集,就是元素的个数。例如 |{1,2,3}| = 3。对于无穷集,基数的概念更复杂(这就是康托尔研究的问题),但本章只涉及有限集。
1.2 集合运算
为什么需要运算?
光有集合还不够——我们经常需要组合集合。比如"选了数学课的同学"是一个集合,“选了物理课的同学"是另一个集合,我想知道"两门都选了的同学"或"至少选了一门的同学”。这就需要集合之间的运算。
核心工具
并集(Union):把两个集合的所有元素合在一起(重复的只算一次)。
1 | A ∪ B = {x | x ∈ A 或 x ∈ B} |
直觉:把两个袋子里的东西倒进一个大袋子。如果 A = {1,2,3},B = {3,4,5},则 A ∪ B = {1,2,3,4,5}。注意 3 只出现一次——集合不重复计数。
“或"的含义:数学中的"或"是包含性或(inclusive or),意思是"至少一个成立”。x ∈ A 或 x ∈ B 包括三种情况:只在 A 中、只在 B 中、同时在 A 和 B 中。
交集(Intersection):两个集合的公共部分——同时属于两个集合的元素。
1 | A ∩ B = {x | x ∈ A 且 x ∈ B} |
直觉:两个圈子重叠的部分。如果 A = {1,2,3},B = {3,4,5},则 A ∩ B = {3}。
如果 A ∩ B = ∅,即两个集合没有公共元素,我们说 A 和 B 不相交(disjoint)。
差集(Difference):从 A 中去掉 B 的部分。
1 | A - B = {x | x ∈ A 且 x ∉ B} |
直觉:A 减去 B。如果 A = {1,2,3},B = {3,4,5},则 A - B = {1,2}(去掉公共的 3),B - A = {4,5}。
注意:A - B ≠ B - A。减法不满足交换律。
补集(Complement):全集中"不是 A"的部分。
1 | A' = U - A = {x | x ∈ U 且 x ∉ A} |
直觉:A 的补集就是"全集中去掉 A 剩下的"。如果 U = {1,2,3,4,5,6,7,8,9,10},A = {1,2,3},则 A’ = {4,5,6,7,8,9,10}。
有时也记作 A̅ 或 Aᶜ。
对称差(Symmetric Difference):两个集合中"只属于其中一个"的部分。
1 | A ⊕ B = (A - B) ∪ (B - A) |
直觉:去掉公共部分,只保留各自独有的。如果 A = {1,2,3},B = {3,4,5},则 A ⊕ B = {1,2,4,5}。
另一种理解:A ⊕ B = (A ∪ B) - (A ∩ B),即"并集减去交集"。
幂集(Power Set):A 的所有子集构成的集合。
1 | P(A) = A 的所有子集的集合 |
如果 A = {1, 2},那么 A 的子集有:∅(空集是任何集合的子集)、{1}、{2}、{1,2}(集合本身也是自己的子集)。
所以 P(A) = {∅, {1}, {2}, {1,2}}。关键公式:如果 A 有 n 个元素,P(A) 有 2^n 个元素。
为什么?对每个元素,你有两个选择——“选它进子集"或"不选它”。n 个元素,每个有 2 种选择,总共 2 × 2 × … × 2 = 2^n 种组合。注意:P(A) 的元素本身也是集合。P(A) = {∅, {1}, {2}, {1,2}} 有 4 个元素,每个元素都是一个集合。
文氏图
文氏图(Venn Diagram):用图形来表示集合及其运算的工具。画法是:用一个矩形框表示全集 U,用圆圈表示 U 中的子集。圆圈的位置关系表示集合之间的关系。
1 | ┌──────────────────────────────┐ |
文氏图的好处是直观——画个图就能看出运算结果。缺点是不够严格,考试中可以用来辅助理解或快速检验,但正式证明不能只靠画图。
运算性质
集合运算满足一些重要的性质。这些性质不是随便记住的——它们是可以证明的(用元素分析法,见下一节)。先直观理解每个性质的含义:
交换律:运算的顺序不影响结果。
1 | A ∪ B = B ∪ A ← 并集:先倒 A 再倒 B,和先倒 B 再倒 A,结果一样 |
对比:减法不满足交换律——A - B ≠ B - A。所以交换律不是所有运算都有的,需要验证。
结合律:连续做同一种运算时,先算哪两个不影响结果。
1 | (A ∪ B) ∪ C = A ∪ (B ∪ C) ← 三个集合取并,先并前两个再并第三个 = 先并后两个再并第一个 |
直觉:就像加法 (1+2)+3 = 1+(2+3) = 6,括号的位置不影响结果。
分配律:一种运算对另一种运算的"分配"。
1 | A ∪ (B ∩ C) = (A ∪ B) ∩ (A ∪ C) ← 并集对交集的分配 |
类比:乘法对加法的分配律 a × (b + c) = a×b + a×c。集合运算中,∪ 和 ∩ 互相可以分配给对方。
德摩根律(De Morgan’s Laws):补集与并/交运算的关系。
1 | (A ∪ B)' = A' ∩ B' ← "不是(A或B)" = "不是A 且 不是B" |
直觉理解:假设 A = “会游泳的人”,B = “会骑车的人”。
(A ∪ B)’ = “不会游泳也不会骑车的人”
A’ ∩ B’ = “不会游泳的人” ∩ “不会骑车的人” = “不会游泳也不会骑车的人”
两者相等 ✓德摩根律在逻辑学、电路设计、编程中极其重要——它告诉你怎么把"否定"推进括号里面。
吸收律:一个集合"吸收"了与自己的交/并。
1 | A ∪ (A ∩ B) = A ← A 并上"A 和 B 的公共部分",结果还是 A |
直觉:A ∩ B 是 A 的子集,所以 A ∪ (A 的子集) = A。就像你已经有一堆东西,再加进去一些你本来就有的,总量不变。
1.3 集合恒等式与证明
为什么需要证明?
光记住公式不够——考试中经常出现"证明某等式成立"的题目。而且证明过程本身训练你的逻辑思维能力。
什么是"证明"? 证明不是"我觉得对",也不是"举几个例子都对"。证明是用逻辑推理,从已知条件出发,一步步推导出结论,使得每一步都无可辩驳。
证明方法
方法一:文氏图法(直观但不够严格)
画图验证两边表示的区域相同。适用于快速检验——先用文氏图看看等式是否大概率成立,但不能作为正式证明。因为文氏图只能表示有限个具体例子,不能证明"对所有情况都成立"。
方法二:元素分析法(最常用、最严格)
证明两个集合相等的标准方法。思路如下:
要证明 A = B,只需证明"对任意元素 x,x ∈ A 当且仅当 x ∈ B"。
"当且仅当"是什么意思?就是两个方向都要证明:
- 方向1(⟹):如果 x ∈ A,那么 x ∈ B
- 方向2(⟸):如果 x ∈ B,那么 x ∈ A
⟺ 这个符号就是"当且仅当"(if and only if)的缩写。A ⟺ B 意味着 A 和 B 同时为真或同时为假。
示例:证明分配律 A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C)
整体分析:我们要证明两个集合相等。按照元素分析法,需要证明对任意 x,x 属于左边当且仅当 x 属于右边。
详解:
设 x 是任意一个元素(我们不知道 x 具体是什么,只知道它要么在左边要么不在)。
方向 1(⟹):假设 x ∈ A ∩ (B ∪ C),证明 x ∈ (A ∩ B) ∪ (A ∩ C)
根据交集的定义,x ∈ A ∩ (B ∪ C) 意味着:
- x ∈ A 并且 x ∈ B ∪ C
根据并集的定义,x ∈ B ∪ C 意味着:
- x ∈ B 或者 x ∈ C(至少一个成立)
现在分两种情况讨论(这是数学中常用的"分类讨论"策略):
- 情况一:x ∈ B。此时我们有 x ∈ A(已知)且 x ∈ B(情况一的条件),所以 x ∈ A ∩ B。既然 x ∈ A ∩ B,那么 x ∈ (A ∩ B) ∪ (A ∩ C)(因为并集只要求至少一个成立)。
- 情况二:x ∈ C。此时我们有 x ∈ A(已知)且 x ∈ C(情况二的条件),所以 x ∈ A ∩ C。既然 x ∈ A ∩ C,那么 x ∈ (A ∩ B) ∪ (A ∩ C)。
无论哪种情况,都有 x ∈ (A ∩ B) ∪ (A ∩ C)。方向 1 证毕。
方向 2(⟸):假设 x ∈ (A ∩ B) ∪ (A ∩ C),证明 x ∈ A ∩ (B ∪ C)
根据并集的定义,x ∈ (A ∩ B) ∪ (A ∩ C) 意味着:
- x ∈ A ∩ B 或者 x ∈ A ∩ C
分两种情况:
- 情况一:x ∈ A ∩ B。这意味着 x ∈ A 且 x ∈ B。既然 x ∈ B,那么 x ∈ B ∪ C。所以 x ∈ A 且 x ∈ B ∪ C,即 x ∈ A ∩ (B ∪ C)。
- 情况二:x ∈ A ∩ C。这意味着 x ∈ A 且 x ∈ C。既然 x ∈ C,那么 x ∈ B ∪ C。所以 x ∈ A 且 x ∈ B ∪ C,即 x ∈ A ∩ (B ∪ C)。
无论哪种情况,都有 x ∈ A ∩ (B ∪ C)。方向 2 证毕。
结论:两个方向都证明了,所以 A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C)。∎
容易犯的错误:
- 只证明了一个方向——必须两个方向都证。
- 直接写"A ∩ (B ∪ C) = A ∩ B ∪ A ∩ C"就完事了——这不是证明,只是把公式抄了一遍。证明必须展示每一步为什么成立,引用了哪个定义。
- 用具体数字代替抽象元素——比如用 x=1 来验证。这只能说明 x=1 时成立,不能说明对所有 x 都成立。
例题与详解
例题 1:集合运算
设全集 U = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},A = {1, 2, 3, 4, 5},B = {3, 4, 5, 6, 7}。求 A ∩ B, A ∪ B, A - B, A’。
整体分析
这道题在考什么?——基本的集合运算。每一步直接套定义即可。
详解
-
A ∩ B:同时属于 A 和 B 的元素。逐个检查:1∈A 但 1∉B,2∈A 但 2∉B,3∈A 且 3∈B ✓,4∈A 且 4∈B ✓,5∈A 且 5∈B ✓。所以 A ∩ B = {3, 4, 5}。
-
A ∪ B:属于 A 或属于 B 的元素(合并起来,去掉重复)。A 有 {1,2,3,4,5},B 有 {3,4,5,6,7},合并得 {1,2,3,4,5,6,7}。
-
A - B:在 A 中但不在 B 中的元素。1∈A 且 1∉B ✓,2∈A 且 2∉B ✓,3∈A 但 3∈B ✗,4∈A 但 4∈B ✗,5∈A 但 5∈B ✗。所以 A - B = {1, 2}。
-
A’:全集中不在 A 中的元素。U = {1,…,10},A = {1,…,5},所以 A’ = {6, 7, 8, 9, 10}。
例题 2:幂集与计数
设 A = {a, b, c},求 P(A) 并验证 |P(A)| = 2^|A|。
整体分析
这道题在考什么?——幂集的构造。需要列出 A 的所有子集。
详解
列出 A 的所有子集,按子集大小分类:
- 含 0 个元素:∅(空集是任何集合的子集)—— 1 个
- 含 1 个元素:{a}, {b}, {c} —— 3 个
- 含 2 个元素:{a,b}, {a,c}, {b,c} —— 3 个
- 含 3 个元素:{a,b,c}(集合本身也是自己的子集)—— 1 个
所以 P(A) = {∅, {a}, {b}, {c}, {a,b}, {a,c}, {b,c}, {a,b,c}}
共 1 + 3 + 3 + 1 = 8 个。
验证公式:|A| = 3,2^|A| = 2³ = 8。✓
为什么是 2^n? 对于每个元素,你有两个选择:把它放进子集,或不放。三个元素就是 2 × 2 × 2 = 8 种组合。这也解释了为什么含 k 个元素的子集有 C(n,k) 个(组合数,后面的章节会学到)——从 n 个元素中选 k 个。
本章速查表
| 概念 | 定义/公式 | 一句话解释 |
|---|---|---|
| 集合 | 一些确定的、可区分的对象的整体 | “一群东西” |
| 元素 | 集合中的对象 | “袋子里的东西” |
| 子集 | A ⊆ B:A 的每个元素都在 B 中 | “A 是 B 的一部分” |
| 真子集 | A ⊂ B:A ⊆ B 且 A ≠ B | “A 是 B 的真子集” |
| 空集 | ∅:没有任何元素的集合 | “空袋子” |
| 并集 | A ∪ B = {x | x∈A 或 x∈B} | “合在一起” |
| 交集 | A ∩ B = {x | x∈A 且 x∈B} | “公共部分” |
| 差集 | A - B = {x | x∈A 且 x∉B} | “A 去掉 B” |
| 补集 | A’ = U - A | “不是 A 的部分” |
| 对称差 | A ⊕ B = (A-B) ∪ (B-A) | “各自独有的” |
| 幂集 | P(A) = A 的所有子集 | “子集的集合”,|P(A)| = 2^n |
| 德摩根律 | (A∪B)’ = A’∩B’,(A∩B)’ = A’∪B’ | “否定推进去,∪∩互换” |
| 空虚真 | 前提不成立时,"如果…那么…"自动为真 | “不可能违反的承诺” |
第2章 命题逻辑
本章定位:从"是什么"(集合描述事物)到"对不对"(逻辑判断真假)。本章不假设你有任何逻辑学的前置知识。
驱动问题
你的电脑 CPU 里面只有两种信号:高电平和低电平(0 和 1)。怎么用这两个信号实现加法、判断、控制?
答案是逻辑门——AND 门、OR 门、NOT 门。这些门的数学基础就是命题逻辑。你写的每一行代码中的 if (a > 0 && b < 10) ,底层都是命题逻辑在运算。
一个更古老的问题:古希腊的**"骑士与无赖"谜题**。岛上只有骑士(只说真话)和无赖(只说假话)。A 说:"我们两个都是无赖。"请问 A 是什么身份?
这种问题靠直觉猜很累,但用命题逻辑可以机械化地求解——这就是形式化推理的力量。
2.1 命题与联结词
什么是命题?
命题(Proposition):一个能判断真假的陈述句。命题要么为真(记作 T 或 1),要么为假(记作 F 或 0),不能既真又假,也不能无法判断。
1 | ✅ 命题:"北京是中国的首都"——这是一个陈述句,可以判断真假(为真) |
易混淆点:假命题也是命题。命题的关键是"能判断真假",不一定是真。"地球是平的"是一个命题——它是一个假命题。
命题变元
命题变元(Propositional Variable):用来代表命题的符号,通常用小写字母 p, q, r, … 表示。
直觉:命题变元就像代数中的变量 x, y, z。在代数中,x 可以代表任何数字;在逻辑中,p 可以代表任何命题。
命题变元和含变量的表达式的区别:命题变元代表一个完整的、有真假值的命题(如"今天下雨"),而含变量的表达式(如 x + 1 = 3)不是一个命题——它需要先给 x 赋值才能判断真假。
联结词
单个命题不够用——我们需要把命题组合起来。就像用 +, -, ×, ÷ 把数字组合成表达式一样,我们需要用联结词把命题组合成复合命题。
五大联结词:
否定(Negation)¬P:“非 P”——把真变成假,把假变成真。
1 | ¬ 是一个一元运算符(只作用于一个命题),类似数学中的负号。 |
真值表是什么?真值表是列出命题变元所有可能的真假组合,以及复合命题在每种组合下的真假值的表格。它是判断逻辑公式的最基本工具。
合取(Conjunction)P ∧ Q:“P 且 Q”——只有当 P 和 Q 都为真时,P ∧ Q 才为真。
1 | ∧ 类似数学中的乘法——"全真才真,有假就假"。 |
析取(Disjunction)P ∨ Q:“P 或 Q”——只要 P 和 Q 至少一个为真,P ∨ Q 就为真。
1 | ∨ 类似数学中的加法——"有真就真,全假才假"。 |
蕴涵(Implication)P → Q:“如果 P,那么 Q”——只有当 P 为真而 Q 为假时,P → Q 才为假。
1 | → 是最容易搞混的联结词。先看真值表: |
等价(Biconditional)P ↔ Q:“P 当且仅当 Q”——P 和 Q 真假相同。
1 | ↔ 的意思是"两边等价"。 |
联结词的优先级
当表达式中没有括号时,按以下优先级计算(优先级从高到低):
1 | ¬(最高)→ ∧ → ∨ → → → ↔(最低) |
例如 P ∧ Q → ¬R ∨ S 等价于 (P ∧ Q) → ((¬R) ∨ S)。
不确定的时候,加括号是最安全的做法。
2.2 命题公式与等值演算
命题公式
命题公式(Propositional Formula):由命题变元和联结词按照语法规则构成的表达式。
构造规则(递归定义):
- 单独一个命题变元(如 p)是公式
- 如果 A 是公式,则 ¬A 也是公式
- 如果 A、B 是公式,则 (A ∧ B)、(A ∨ B)、(A → B)、(A ↔ B) 也是公式
- 只有通过以上规则构造出来的才是公式
1 | ✅ 合法公式:p, ¬p, (p ∧ q), (p → (q ∨ r)) |
赋值与真值
赋值(Assignment / Valuation):给公式中每个命题变元指定一个真假值(T 或 F)。
例如,公式 p ∧ q → ¬r 有三个变元 p, q, r。一种赋值是 p=T, q=F, r=T。在这种赋值下,公式的值可以计算出来。
重言式(Tautology,也叫永真式):在所有可能的赋值下都为真的公式。
1 | 例如:p ∨ ¬p("p 或非 p")——无论 p 是真还是假,结果都为真。 |
矛盾式(Contradiction,也叫永假式):在所有可能的赋值下都为假的公式。
1 | 例如:p ∧ ¬p("p 且非 p")——无论 p 是真还是假,结果都为假。 |
可满足式(Satisfiable Formula):至少存在一种赋值使其为真。重言式也是可满足式,但可满足式不一定是重言式。
1 | 例如:p ∧ q 是可满足式(当 p=T, q=T 时为真),但不是重言式(当 p=T, q=F 时为假)。 |
等值式
等值(Logical Equivalence):如果两个公式在所有可能的赋值下真值都相同,称它们等值,记作 A ⟺ B。
⟺ 读作"当且仅当"或"等值于"。在第1章中我们见过这个符号——那里表示集合相等,这里表示逻辑等值。含义类似:两者"本质上相同"。
核心等值式(考试高频,必须熟练):
| 名称 | 公式 | 为什么成立 |
|---|---|---|
| 双重否定 | ¬¬p ⟺ p | 否定两次等于没否定 |
| 德摩根律 | ¬(p ∧ q) ⟺ ¬p ∨ ¬q | “不是(p且q)” = “不是p或不是q” |
| 德摩根律 | ¬(p ∨ q) ⟺ ¬p ∧ ¬q | “不是(p或q)” = “不是p且不是q” |
| 蕴涵消去 | p → q ⟺ ¬p ∨ q | “如果p则q” = “非p或q” |
| 等价展开 | p ↔ q ⟺ (p → q) ∧ (q → p) | “当且仅当” = 双向蕴涵 |
| 分配律 | p ∧ (q ∨ r) ⟺ (p ∧ q) ∨ (p ∧ r) | 类似乘法分配律 |
| 吸收律 | p ∧ (p ∨ q) ⟺ p | p "吸收"了 p∨q 中的自己 |
蕴涵消去是推理的核心工具:p → q ⟺ ¬p ∨ q。这意味着"如果p则q"等价于"非p或q"。证明:两者的真值表完全相同。这个等值式允许我们把 → 消掉,用 ¬ 和 ∨ 代替。
等值演算的方法
用已知的等值式,对公式进行逐步替换,直到化简为目标形式。每一步都要说明用了哪个等值式。
示例:证明 p → (q → r) ⟺ (p ∧ q) → r
整体分析:目标是证明两个公式等值。策略是从左边出发,一步步化简到右边。
1 | p → (q → r) |
每一步都有依据,这就是等值演算——用已知的等值式做"合法的替换"。
2.3 范式
为什么需要范式?
两个公式看起来完全不同(比如 p → q 和 ¬p ∨ q),怎么判断它们是否等值?我们需要一种标准形式——把任何公式都化归到同一个框架下比较。就像数学中的"化简"——每个表达式化简后只有一个标准形式。
核心概念
文字(Literal):一个命题变元或其否定。p 和 ¬p 都是文字。
例如:p, ¬p, q, ¬q, r 都是文字。
小项(Minterm):所有命题变元的合取(且),每个变元恰好出现一次(可以是否定的形式)。
例如有两个变元 p, q 时,小项有 4 个:
- p ∧ q(两个都不否定)
- p ∧ ¬q(q 否定)
- ¬p ∧ q(p 否定)
- ¬p ∧ ¬q(两个都否定)
为什么叫"小项"?因为每个小项只在一种赋值下为真(范围最小)。p ∧ q 只在 p=T, q=T 时为真。
如果有 n 个变元,就有 2^n 个小项(每个变元两种选择:是否否定)。
大项(Maxterm):所有命题变元的析取(或),每个变元恰好出现一次。
例如有两个变元 p, q 时,大项有 4 个:
- p ∨ q
- p ∨ ¬q
- ¬p ∨ q
- ¬p ∨ ¬q
为什么叫"大项"?因为每个大项只在一种赋值下为假(范围最大,在其他所有赋值下都为真)。p ∨ q 只在 p=F, q=F 时为假。
析取范式(DNF,Disjunctive Normal Form):小项的析取(或)。形如 (…) ∨ (…) ∨ …,每个括号内是文字的合取。
1 | 例如:(p ∧ q) ∨ (p ∧ ¬q) ∨ (¬p ∧ q) |
合取范式(CNF,Conjunctive Normal Form):大项的合取(且)。形如 (…) ∧ (…) ∧ …,每个括号内是文字的析取。
1 | 例如:(p ∨ q) ∧ (p ∨ ¬q) ∧ (¬p ∨ q) |
记忆技巧:析取范式 = “析取在外面” = (合取) ∨ (合取) ∨ …
合取范式 = “合取在外面” = (析取) ∧ (析取) ∧ …
主析取范式(主DNF):所有使公式为真的小项的析取。它是唯一的——每个公式的主析取范式只有一个。
主合取范式(主CNF):所有使公式为假的大项的合取。它也是唯一的。
为什么主范式是唯一的?因为"使公式为真的赋值组合"是确定的,对应的小项也是确定的。
因此:两个公式等值 ⟺ 它们的主析取范式相同(或主合取范式相同)。这是判断等值的终极方法。
求主析取范式的方法
方法一:真值表法(最直观)
- 列出公式的真值表
- 找出所有使公式为真的行
- 每行对应一个小项,把这些小项用 ∨ 连接起来
示例:求 p → q 的主析取范式
1 | | p | q | p → q | |
主析取范式 = (p ∧ q) ∨ (¬p ∧ q) ∨ (¬p ∧ ¬q)
验证:当 p=T, q=T 时,(p ∧ q) 为真,整个析取为真 ✓;当 p=T, q=F 时,三个小项都为假,整个析取为假 ✓(与 p → q 的真值表一致)。
方法二:等值演算法
用等值式逐步展开,直到每个合取式中包含所有变元。
2.4 联结词全功能集与组合电路
联结词全功能集
问题:五个联结词 ¬, ∧, ∨, →, ↔ 全都需要吗?能不能只用其中一部分,表达所有公式?
答案:可以。一个联结词集合如果能表达所有命题公式,就叫全功能集(Functionally Complete Set)。
| 全功能集 | 说明 |
|---|---|
| {¬, ∧, ∨} | 最常用的全功能集 |
| {¬, ∧} | 因为 p ∨ q ⟺ ¬(¬p ∧ ¬q)(德摩根律) |
| {¬, ∨} | 因为 p ∧ q ⟺ ¬(¬p ∨ ¬q)(德摩根律) |
| {¬, →} | 因为 p ∨ q ⟺ ¬p → q,p ∧ q ⟺ ¬(p → ¬q) |
| {↑}(与非) | 单个联结词就够!p ↑ q ⟺ ¬(p ∧ q) |
| {↓}(或非) | 也够!p ↓ q ⟺ ¬(p ∨ q) |
工程意义:实际电路中,只需要一种逻辑门(如 NAND 门)就能实现所有逻辑功能。这就是为什么 CPU 可以只用一种基本门来构建——降低成本、简化设计。
组合电路
命题公式和电路之间有直接对应关系:
| 逻辑符号 | 电路元件 | 含义 |
|---|---|---|
| ¬p | NOT 门 | 输入取反 |
| p ∧ q | AND 门 | 两个输入都为1时输出1 |
| p ∨ q | OR 门 | 至少一个输入为1时输出1 |
| p → q | 等价于 ¬p ∨ q | 用 NOT + OR 门实现 |
| p ↑ q | NAND 门 | 先 AND 再 NOT |
每个命题公式都可以画成一个电路图。反过来,每个组合电路也可以用命题公式表示。这就是命题逻辑和数字电路之间的桥梁。
2.5 推理理论
什么是推理?
推理(Inference):从一些已知的命题(叫做前提)出发,按照逻辑规则推导出新命题(叫做结论)的过程。
有效推理:如果所有前提都为真,那么结论一定为真。推理是否有效与前提实际上是否为真无关——只关心"如果前提为真,结论是否必然为真"。
区分"有效"和"正确":推理有效是指形式上没问题;推理正确是指前提实际上为真且推理有效。
核心推理规则
| 规则 | 形式 | 含义 | 例子 |
|---|---|---|---|
| 假言推理 | p → q, p ⊢ q | 如果p则q,p成立,所以q成立 | 如果下雨地湿,下雨了,所以地湿 |
| 拒取式 | p → q, ¬q ⊢ ¬p | 如果p则q,q不成立,所以p不成立 | 如果下雨地湿,地没湿,所以没下雨 |
| 假言三段论 | p → q, q → r ⊢ p → r | 条件链传递 | 如果A则B,如果B则C,所以如果A则C |
| 析取三段论 | p ∨ q, ¬p ⊢ q | 两者至少一个,p不是,那一定是q | 会游泳或会骑车,不会游泳,所以会骑车 |
| 合取引入 | p, q ⊢ p ∧ q | 两者都成立,合取也成立 | p真,q真,所以p∧q真 |
| 化简 | p ∧ q ⊢ p | 合取成立,各部分都成立 | p∧q真,所以p真 |
假言推理是最常用的推理规则,你每天都在用。代码中的
if语句就是假言推理——条件满足,执行后果。符号 ⊢ 读作"推出"或"可证"。p → q, p ⊢ q 的意思是"已知 p → q 和 p,可以推出 q"。
例题与详解
例题 1:骑士与无赖
岛上有 A、B 两人,每人要么是骑士(只说真话),要么是无赖(只说假话)。A 说:"我们两个都是无赖。"请问 A 和 B 各是什么身份?
整体分析
这道题在考什么?——用命题逻辑形式化一个谜题,然后用推理规则求解。
详解
第一步:符号化
设 p = “A 是骑士”,q = “B 是骑士”。
- 骑士说真话:如果 A 是骑士,他说的话为真
- 无赖说假话:如果 A 是无赖,他说的话为假
A 说的话"我们两个都是无赖"等价于:¬p ∧ ¬q(A不是骑士 且 B不是骑士)。
第二步:建立推理
我们不知道 A 是骑士还是无赖,所以分两种情况:
情况一:假设 A 是骑士(p 为真)
- 骑士说真话,所以 ¬p ∧ ¬q 必须为真
- ¬p ∧ ¬q 为真意味着 ¬p 为真,即 p 为假
- 但我们的假设是 p 为真,矛盾!
- 情况一不可能成立
情况二:假设 A 是无赖(p 为假)
- 无赖说假话,所以 ¬p ∧ ¬q 必须为假
- ¬p 为真(因为 p 为假),要使 ¬p ∧ ¬q 为假,必须 ¬q 为假
- ¬q 为假意味着 q 为真,即 B 是骑士
- 验证:A 说"都是无赖",实际 A 是无赖、B 是骑士,A 说的是假话 ✓(无赖说假话,符合)
结论:A 是无赖,B 是骑士。
这道题展示了命题逻辑的力量——把自然语言转化为符号,然后用逻辑规则机械化地推理,不需要直觉猜测。
例题 2:化归主析取范式
用等值演算法求 (p → q) ∧ q 的主析取范式。
整体分析
策略:先化简公式,再展开为小项的析取。
详解
1 | (p → q) ∧ q |
等等,结果是 q。现在需要把 q 展开为含 p 的主析取范式:
1 | q |
所以主析取范式 = (p ∧ q) ∨ (¬p ∧ q)
真值表验证:
p q (p→q)∧q p∧q ¬p∧q (p∧q)∨(¬p∧q) T T T T F T T F F F F F F T T F T T F F T F F F 等等,(p→q)∧q 在 p=F, q=F 时应该是 (T→F)∧F = F∧F = F?不对——(F→F) = T,T∧F = F。
实际上 (p→q)∧q 在 p=F, q=T 时 = (F→T)∧T = T∧T = T。
我的真值表验证结果一致 ✓。
本章速查表
| 概念 | 定义/公式 | 一句话解释 |
|---|---|---|
| 命题 | 能判断真假的陈述句 | “有真假的句子” |
| 命题变元 | 代表命题的符号 p, q, r | “逻辑中的变量” |
| ¬p | 否定 | “反过来” |
| p ∧ q | 合取 | “且”,全真才真 |
| p ∨ q | 析取 | “或”,有真就真 |
| p → q | 蕴涵 | “如果p则q”,只在p真q假时为假 |
| p ↔ q | 等价 | “当且仅当”,同真假 |
| 重言式 | 所有赋值下都为真 | “永远对” |
| 矛盾式 | 所有赋值下都为假 | “永远错” |
| 等值 | 所有赋值下真值相同 | “本质相同” |
| 蕴涵消去 | p → q ⟺ ¬p ∨ q | “把→消掉” |
| 小项 | 所有变元的合取 | “范围最小的命题” |
| 大项 | 所有变元的析取 | “范围最大的命题” |
| 主析取范式 | 使公式为真的所有小项的析取 | “标准形式” |
| 全功能集 | 能表达所有公式的联结词集合 | “最少够用的联结词” |
| 假言推理 | p→q, p ⊢ q | “条件满足就执行” |
| 拒取式 | p→q, ¬q ⊢ ¬p | “结果没发生,条件也没发生” |
第3章 一阶逻辑
本章定位:命题逻辑的表达力到头了——我们需要更深的语言来处理"所有人""存在某个"这类结构。一阶逻辑就是为此而生的。本章不假设你已经熟悉命题逻辑,必要时会简要回顾。
驱动问题
命题逻辑的一个致命缺陷:它看不懂句子的内部结构。
在第2章的命题逻辑中,"所有人都会死"被当作一个原子命题 P——一个不可拆分的黑盒。我们无法进一步追问:“所有人"具体指谁?能不能拆成"每个人分别都会死”?
更具体地说,假设你想表达以下三个句子:
- “所有人都会死”
- “存在一个不死的人”
- “对任意一个 x,如果 x 是人,那么 x 会死”
命题逻辑只能给每个句子分配一个字母(P、Q、R),然后说"P 是一个命题"。但它完全无法看到这些句子之间的逻辑关系——比如句子 1 和句子 2 其实是互相矛盾的。
现实映射:你每天都在用一阶逻辑。数据库查询 SELECT * FROM students WHERE age > 18 的意思是:"对于 students 表中的每一行,如果 age > 18,就选出来。"这里的"每一行"就是全称量词(遍历论域),"如果…就…"是谓词的筛选。搜索引擎、SQL 查询、编程中的 for 循环和 filter 操作,底层都是一阶逻辑的思想。
3.1 基本概念
从命题逻辑的困境说起
在命题逻辑中,我们有命题变元(propositional variable)——用字母 p、q、r 代表一个完整的句子,以及联结词(connective)——¬(非)、∧(且)、∨(或)、→(如果…则…)、↔(当且仅当),把命题组合成复合命题。
但命题逻辑有一个根本限制:命题变元是不可分割的原子。它只知道 p 为真或为假,不知道 p 的内部是什么。
直觉类比:命题逻辑看一个句子,就像你看一个不透明的快递箱子——你只知道"箱子到了"(真)或"箱子没到"(假),但不知道箱子里装的是什么。一阶逻辑则是打开了箱子,看到了里面的零件——个体、谓词、量词。
个体
个体(Individual):我们所讨论的具体对象。也叫个体常元(individual constant)或常量。
1 | "张三是一个学生"中的"张三"是一个个体。 |
直觉:个体就是你讨论的那个"东西"——一个人、一个数、一个物体,任何你能指名道姓的对象。
在符号化时,我们通常用小写字母 a、b、c 等表示个体常元。例如设 a = 张三,b = 李四。
个体变元(Individual Variable):用来泛指"某个个体"的变量,通常用 x、y、z 表示。它不特指某个对象,而是代表论域中的任意一个元素。
直觉:个体变元和个体常元的关系,就像代数中 x 和具体数字 3 的关系。x 可以是任何数,但 3 就是 3。
谓词
谓词(Predicate):描述个体具有某种性质或个体之间存在某种关系的陈述。谓词本身不是命题——它含有变量,变量赋值之后才变成有真假的命题。
1 | 设 P(x) 表示 "x 是偶数"。 |
直觉类比:谓词就像一个"检测机器"。你把一个对象塞进去,它吐出"真"或"假"。P(x) = “x 是偶数” 就是一台"偶数检测机"——塞 2 进去,绿灯亮(真);塞 3 进去,红灯亮(假)。
命题和谓词的关系:命题是谓词的特例——把谓词中的所有变量都填上具体值,它就变成了命题。P(2) 是命题,P(x) 不是。
谓词可以有多个变量。变量的个数叫做谓词的元数(arity)。
1 | 一元谓词(一个变量): |
为什么需要多元谓词?因为很多关系涉及两个或多个对象。“x 大于 y” 涉及两个对象,不能用一元谓词表达。你需要一个二元谓词来同时描述 x 和 y 之间的关系。
论域
论域(Domain of Discourse,也叫个体域):个体变元的取值范围——即我们在讨论"什么东西"。
直觉类比:论域是一个"舞台",所有个体都在这个舞台上活动。量词遍历的就是这个舞台上的所有对象。
论域不是固定的——取决于你在讨论什么:
- 讨论"数的性质"时,论域可能是所有自然数 {0, 1, 2, 3, …}
- 讨论"班里的同学"时,论域可能是 {张三, 李四, 王五, …}
- 讨论"所有人"时,论域是世界上所有人的集合
同一个公式在不同论域下可能有不同的真假值:
1 | ∀x (x² ≥ 0) |
关键原则:在符号化一个公式之前,必须先明确论域是什么。否则公式没有确定的含义。
量词
现在来到本章最核心的概念——量词(Quantifier)。量词用来表达"对所有"和"存在"这两种基本的量化思想。
为什么需要量词? 回到驱动问题。命题逻辑中,"所有人都会死"是一个不可拆分的原子命题。但实际上,这句话的意思是:"对于论域中的每一个个体 x,如果 x 是人,那么 x 会死。“我们需要一种工具来表达"每一个"和"存在”——这就是量词。
全称量词(Universal Quantifier)∀:表示"对论域中的所有个体"。∀x 读作"对任意 x"或"对所有 x"。
存在量词(Existential Quantifier)∃:表示"论域中存在至少一个个体"。∃x 读作"存在 x"或"存在某个 x"。
直觉类比:
- ∀x 就像一个
for each循环——遍历论域中的每一个元素,检查某个条件是否成立- ∃x 就像一个
exists查询——在论域中找到一个满足条件的元素就够了
例子(设论域 = 所有人,H(x) = “x 是人”,M(x) = “x 会死”):
1 | ∀x (H(x) → M(x)) |
重要:全称量词通常与蕴涵 → 搭配,存在量词通常与合取 ∧ 搭配。
为什么?“所有人都会死"的意思是"如果你是一个人,那么你会死”——这是"如果…则…"的结构,即 →。
“存在不死的人"的意思是"有一个人,他既是人又不会死”——这是"且"的结构,即 ∧。⚠️ 初学者常犯的错误:写成 ∀x (H(x) ∧ M(x))。这表示"所有 x 都是人且会死"——论域中的每个东西都是人。如果论域里有石头,这就假了。正确写法是 ∀x (H(x) → M(x))。
量词的顺序很重要!改变量词的顺序可能改变命题的含义。
1 | 设论域 = 自然数 {0, 1, 2, 3, ...},G(x, y) 表示 "x > y"。 |
直觉记忆:∀ 在前 ∃ 在后相当于"给每个人分配一个助手"——不同的人可以有不同的助手;∃ 在前 ∀ 在后相当于"找一个万能助手服务所有人"——这难得多。
用
for循环类比:
- ∀x ∃y → 外层循环遍历 x,每次内层循环可以选不同的 y
- ∃y ∀x → 先固定一个 y,然后外层循环检查所有 x——y 必须对所有 x 都管用
3.2 合式公式
为什么要定义"公式"?
在命题逻辑中,我们定义了什么是合法的命题公式(如 p ∧ q 是合法的,∧p q 不是)。在一阶逻辑中,我们也需要精确定义什么是合法的表达式——不能随便写,要符合语法规则。
合式公式(Well-Formed Formula,缩写 WFF):符合一阶逻辑语法规则的表达式。也叫良构公式。
直觉:合式公式就是一阶逻辑的"合法句子"。就像自然语言中"猫坐在垫子上"是合法句子,而"坐猫垫子在上"不是——逻辑公式也有语法。
公式的构造规则
一阶逻辑的合式公式通过以下规则递归地构造("递归"的意思是:后面的规则可以使用前面已经构造好的公式):
规则 1:原子公式。如果 P 是一个 n 元谓词符号,t₁, t₂, …, tₙ 是项(个体常元、个体变元或函数作用于项),则 P(t₁, t₂, …, tₙ) 是合式公式。
1 | 设 A(x) 表示 "x 是动物",B(x, y) 表示 "x 比 y 大"。 |
回顾:谓词(predicate)描述个体的性质或关系。A(x) 是一个谓词,A(a) 是把具体个体 a 代入后的命题。
规则 2:如果 A 是合式公式,则 ¬A(A 的否定)也是合式公式。
1 | 如果 A(x) = "x 是动物",则 ¬A(x) = "x 不是动物" |
规则 3:如果 A 和 B 都是合式公式,则 (A ∧ B)、(A ∨ B)、(A → B)、(A ↔ B) 都是合式公式。
回顾:∧(合取,“且”)、∨(析取,“或”)、→(蕴涵,“如果…则…”)、↔(等价,“当且仅当”)是命题逻辑中的联结词,它们在一阶逻辑中继续使用。
规则 4:如果 A 是合式公式,x 是个体变元,则 ∀x A 和 ∃x A 都是合式公式。
1 | 如果 A(x) = "x 是偶数",则: |
规则 5:只有通过以上规则有限次应用得到的才是合式公式。
1 | ✅ 合式公式:A(x), ¬A(x), A(x) ∧ B(x, y), ∀x A(x), ∃x ∀y B(x, y) |
自由变元与约束变元
一个公式中的变量可能处于两种不同的状态,理解这个区别至关重要。
约束变元(Bound Variable):被量词"绑住"的变量。当一个变量出现在 ∀x 或 ∃x 的辖域(scope,即量词后面紧跟的那个子公式)中时,它就是约束变元。
直觉类比:约束变元是"哑变量"(dummy variable),就像数学求和式 Σᵢ₌₁ⁿ aᵢ 中的 i。i 只是一个遍历指标,你可以把 i 换成 j,结果不变。同样,∀x P(x) 和 ∀y P(y) 表达的是同一个意思。
自由变元(Free Variable):不被任何量词约束的变量。自由变元使得公式的真假值依赖于这个变量的具体取值。
直觉:自由变元就像一个"未填的空格"——你需要先给它填上一个值,公式才能判断真假。
例子:
1 | (1) ∀x P(x, y) |
判断技巧:看一个变量是自由还是约束,方法是从该变量的位置往外看,找最近的同名量词。如果找到了,它就是约束的;如果找不到(即一直到公式最外层都没有同名量词),它就是自由的。
闭公式(Closed Formula / Sentence):没有自由变元的公式。闭公式的真假完全确定(在给定论域和解释下)。
开公式(Open Formula):含有自由变元的公式。开公式的真假不确定——需要先给自由变元赋值。
语义解释
给定一个公式,怎么判断它为真还是为假?我们需要一个解释(Interpretation),也叫结构(Structure)。
解释包括三个部分:
- 论域 D:非空集合,规定了讨论的范围
- 个体常元的指派:给每个常元符号指定 D 中的一个元素
- 谓词的含义:给每个谓词符号指定具体的性质或关系
1 | 举例:公式 ∀x P(x, a) |
直觉:解释就像给公式"填表"——告诉公式每个符号具体代表什么。没有解释,公式只是一个空壳。
3.3 一阶逻辑等值式
为什么需要等值式?
在命题逻辑(第2章)中,我们用等值式把公式变换成等价的形式(例如蕴涵消去 p → q ⟺ ¬p ∨ q)。在一阶逻辑中,我们也需要类似的工具来变换公式——特别是处理量词的否定、量词的辖域扩张和收缩。
回顾:等值(equivalence)的意思是两个公式在所有解释下都有相同的真假值。A ⟺ B 表示 A 和 B 等值。⟺ 读作"当且仅当"或"等值于"。
量词否定
这是最重要、最常用的一组等值式。
直觉:否定词 ¬ 穿过量词时,量词要"翻转"——∀ 变成 ∃,∃ 变成 ∀。
1 | ¬∀x P(x) ⟺ ∃x ¬P(x |
“不是所有人都满足 P” 等价于 “存在某个人不满足 P”。
直觉类比:老师说"不是所有同学都及格了"——这意味着什么?必然存在至少一个同学没及格。反过来,“不存在没及格的同学"意味着"所有人都及格了”。
用自然语言验证:
- ¬∀x P(x) = “并非所有人都…”
- ∃x ¬P(x) = “有人不…”
- 这两句话表达的是同一件事。
1 | ¬∃x P(x) ⟺ ∀x ¬P(x) |
“不存在满足 P 的 x” 等价于 “所有 x 都不满足 P”。
类比:“没有人的身高超过 3 米” 等价于 “所有人的身高都不超过 3 米”。
记忆口诀:否定穿过量词,量词翻转(∀ 变 ∃,∃ 变 ∀)。两次应用这个规则:
1 | ¬∀x ∃y P(x, y) |
自然语言:“并非对每个 x 都存在 y 使得 P(x,y)” 等价于 “存在某个 x,对所有 y 都有 ¬P(x,y)”。
量词辖域扩张与收缩
当一个子公式不含有某个变量时,量词可以"伸缩"其辖域。
核心条件:以下等值式成立的前提是 B 中不包含变元 x(即 x 不在 B 中出现)。
全称量词的扩张与收缩:
1 | ∀x (A(x) ∧ B) ⟺ ∀x A(x) ∧ B (B 不含 x) |
1 | ∀x (A(x) → B) ⟺ ∃x A(x) → B (B 不含 x) |
1 | ∀x (B → A(x)) ⟺ B → ∀x A(x) (B 不含 x) |
存在量词的扩张与收缩:
1 | ∃x (A(x) ∨ B) ⟺ ∃x A(x) ∨ B (B 不含 x) |
1 | ∃x (B → A(x)) ⟺ B → ∃x A(x) (B 不含 x) |
1 | ∃x (A(x) → B) ⟺ ∀x A(x) → B (B 不含 x) |
为什么有这个规则? 当 B 不含 x 时,B 的真假不随 x 的变化而变化。所以量词对 B 没有影响——B 可以自由地提到量词外面或者放回量词里面。
类比:假设你在用
for循环遍历数组,循环体内有一个和循环变量无关的常量 c。你可以把 c 提到循环外面,不影响结果。
其他重要等值式
量词对 ∧ 和 ∨ 的分配:
1 | ∀x (A(x) ∧ B(x)) ⟺ ∀x A(x) ∧ ∀x B(x) |
1 | ∃x (A(x) ∨ B(x)) ⟺ ∃x A(x) ∨ ∃x B(x) |
但以下等值式不成立(考试常考的陷阱):
1 | ❌ ∀x (A(x) ∨ B(x)) ⟺ ∀x A(x) ∨ ∀x B(x) |
直觉:左边说"每个数要么奇要么偶"(对的),右边说"要么所有数都是奇数,要么所有数都是偶数"(显然不对)。“每个个体分别满足 A 或 B"不等于"所有个体都满足 A,或所有个体都满足 B”。
1 | ❌ ∃x (A(x) ∧ B(x)) ⟺ ∃x A(x) ∧ ∃x B(x) |
直觉:左边说"有某个东西既奇又偶"(没有),右边说"有某个东西是奇数,且有某个东西是偶数"(对的,1是奇数、2是偶数)。“同一个个体同时满足 A 和 B"不等于"某个个体满足 A,另一个个体满足 B”。
记忆规律:
| 分配类型 | 成立? | 原因 |
|---|---|---|
| ∀ 对 ∧ 分配 | 成立 | “所有人唱歌且跳舞” = “所有人唱歌 且 所有人跳舞” |
| ∃ 对 ∨ 分配 | 成立 | “有人唱歌或跳舞” = “有人唱歌 或 有人跳舞” |
| ∀ 对 ∨ 分配 | 不成立 | “每人唱歌或跳舞” ≠ “所有人唱歌 或 所有人跳舞” |
| ∃ 对 ∧ 分配 | 不成立 | “有人唱歌且跳舞” ≠ “有人唱歌 且 有人跳舞” |
一句话记忆:全称量词分配合取,存在量词分配析取。反过来不行。
3.4 前束范式
为什么需要前束范式?
在命题逻辑(第2章)中,我们用析取范式(DNF)和合取范式(CNF)把公式化为标准形式,方便比较和计算。在一阶逻辑中,我们也需要一种标准形式——前束范式。
直觉:前束范式就是"把所有量词提到最前面",后面跟一个不含任何量词的部分。就像把所有的
for循环写在最外层,循环体里面只有纯逻辑运算。
什么是前束范式?
前束范式(Prenex Normal Form):一个公式如果形如
1 | Q₁x₁ Q₂x₂ ... Qₙxₙ B |
其中每个 Qᵢ 是 ∀ 或 ∃(量词),B 是一个不含量词的命题逻辑公式(只含个体变元、谓词、联结词),则称该公式为前束范式。
“前束"的意思是"量词在前面束起来”——所有量词都集中在公式的最前端。
1 | ✅ 前束范式: |
化归方法
把任意公式化为前束范式的步骤:
步骤 1:消去 → 和 ↔。用等值式把蕴涵和等价替换掉。
回顾(来自命题逻辑):
- 蕴涵消去:A → B ⟺ ¬A ∨ B
- 等价展开:A ↔ B ⟺ (A → B) ∧ (B → A) ⟺ (¬A ∨ B) ∧ (¬B ∨ A)
步骤 2:把否定号 ¬ 移到最内层。用德摩根律(来自命题逻辑:¬(A ∧ B) ⟺ ¬A ∨ ¬B,¬(A ∨ B) ⟺ ¬A ∧ ¬B)和量词否定规则(¬∀x A(x) ⟺ ∃x ¬A(x),¬∃x A(x) ⟺ ∀x ¬A(x))把 ¬ 推到谓词前面。
步骤 3:必要时换名(alpha-conversion)。如果不同量词使用了同一个变量名(可能导致混淆),给其中一个换一个新名字。
回顾:约束变元是哑变量,可以换名。∀x P(x) 和 ∀y P(y) 是同一个意思。
步骤 4:用量词辖域扩张把量词提到最前面。从内到外,逐个把量词提到公式最外面。
详细示例
示例 1:将 ∀x P(x) → ∃x Q(x) 化为前束范式。
整体分析:公式中有 → 需要消去,有两个不同量词都用 x 需要换名,最后把量词提到前面。
1 | ∀x P(x) → ∃x Q(x) |
每一步的依据:
- 步骤 1 用的是命题逻辑的蕴涵消去等值式
- 步骤 2 用的是一阶逻辑的量词否定等值式
- 步骤 3 用的是约束变元可以换名(alpha-conversion)
- 步骤 4 用的是量词辖域扩张等值式
示例 2:将 ∃x P(x) → ∀x (Q(x) → R(x)) 化为前束范式。
1 | ∃x P(x) → ∀x (Q(x) → R(x)) |
示例 3:将 ∀x (P(x) → ∃y Q(x, y)) 化为前束范式。
1 | ∀x (P(x) → ∃y Q(x, y)) |
注意:步骤 2 中,∃y 提到了 ∀x 的后面(不是前面)。前束范式要求量词在最前面,但 ∃y 原本就在 ∀x 的辖域内,所以提出后仍然在 ∀x 之后。
例题与详解
例题 1:自然语言符号化
将以下句子符号化。论域为所有人,设 H(x) = “x 是人”,M(x) = “x 会死”,L(x, y) = “x 爱 y”。
(a) 所有人都会死。
(b) 有些人不会死。
© 没有人爱所有人。
(d) 每个人都爱某个人。
(e) 有一个人被所有人爱。
整体分析
自然语言符号化的关键是:先确定论域,再确定谓词,最后用量词和联结词组合。特别注意全称量词通常与 → 搭配,存在量词通常与 ∧ 搭配。
详解
(a) 所有人都会死。
"所有"→ 全称量词 ∀x。"如果 x 是人,则 x 会死"→ H(x) → M(x)。
1 | ∀x (H(x) → M(x)) |
易错点:不要写成 ∀x (H(x) ∧ M(x))。这表示"所有 x 都是人且会死"——如果论域中有石头,石头也是"人且会死"吗?显然不对。正确的写法用 → 表示"如果是人,那么会死",对非人的 x(如石头),蕴涵的前提为假,整个蕴涵自动为真。
(b) 有些人不会死。
"有些"→ 存在量词 ∃x。"x 是人且 x 不会死"→ H(x) ∧ ¬M(x)。
1 | ∃x (H(x) ∧ ¬M(x)) |
易错点:不要写成 ∃x (H(x) → ¬M(x))。∃ 与 → 搭配会出问题——因为只要存在一个非人的 x(如石头),H(x) 为假,→ 自动为真。这使得几乎任何论域都满足这个公式。
© 没有人爱所有人。
"没有人"→ ¬∃x 或 ∀x ¬。"x 爱所有人"→ ∀y L(x, y)。
1 | ¬∃x (H(x) ∧ ∀y (H(y) → L(x, y))) |
(d) 每个人都爱某个人。
"每个人"→ ∀x。"存在某个人 y 被 x 爱"→ ∃y L(x, y)。
1 | ∀x (H(x) → ∃y (H(y) ∧ L(x, y))) |
(e) 有一个人被所有人爱。
"有一个人"→ ∃x。"所有人爱 x"→ ∀y L(y, x)。
1 | ∃x (H(x) ∧ ∀y (H(y) → L(y, x))) |
例题 2:量词否定的连续应用
化简 ¬∀x ∃y (P(x) → Q(x, y))。
整体分析
否定号需要穿过两个量词。每穿过一个,量词翻转一次。
详解
1 | ¬∀x ∃y (P(x) → Q(x, y)) |
结果的自然语言含义:“存在某个 x,对所有 y,都有 P(x) 成立且 Q(x, y) 不成立。”
例题 3:前束范式
将 ∀x (P(x) ∨ Q(x)) → ∃x P(x) 化为前束范式。
整体分析
公式中有 → 需要消去,量词辖域需要调整。
详解
1 | ∀x (P(x) ∨ Q(x)) → ∃x P(x) |
每一步都用到了一条已知的等值式。前束范化的过程就是等值演算——每一步变换都是"合法的替换",保证变换前后的公式等值。
例题 4:判断自由变元与约束变元
指出以下公式中的自由变元和约束变元:
(a) ∀x (P(x) → Q(x, y))
(b) ∀x P(x) ∧ Q(x)
© ∃x ∀y (R(x, y) → ∃z S(y, z))
详解
(a) ∀x (P(x) → Q(x, y))
- x:被 ∀x 约束(出现在 ∀x 的辖域内)
- y:自由(没有量词约束 y)
- 自由变元:{y},约束变元:{x}
(b) ∀x P(x) ∧ Q(x)
这个公式实际上是 (∀x P(x)) ∧ Q(x),因为量词的辖域只到它后面紧跟的最小子公式。
- 第一个 x(在 P(x) 中):被 ∀x 约束
- 第二个 x(在 Q(x) 中):自由(Q(x) 不在 ∀x 的辖域内)
- 注意:这两个 x 虽然字母相同,但一个是约束的,一个是自由的——它们是不同的变量!
- 自由变元:{x}(Q(x) 中的 x),约束变元:{x}(P(x) 中的 x)
© ∃x ∀y (R(x, y) → ∃z S(y, z))
- x:被 ∃x 约束
- y:被 ∀y 约束
- z:被 ∃z 约束
- 没有自由变元——这是一个闭公式(命题)
本章速查表
| 概念 | 定义/公式 | 一句话解释 |
|---|---|---|
| 个体 | 所讨论的具体对象(常元 a、b、c;变元 x、y、z) | “你在谈论的那个东西” |
| 谓词 | 描述个体性质或关系的表达式 P(x)、R(x,y) | “检测机器:塞进去一个对象,吐出真假” |
| 论域 | 个体变元的取值范围 | “舞台——所有个体都在上面活动” |
| 全称量词 ∀x | 对论域中所有 x | “for each 循环” |
| 存在量词 ∃x | 论域中存在至少一个 x | “exists 查询” |
| ∀x 与 → 搭配 | ∀x (H(x) → M(x)) | “所有人都是…(如果是人则…)” |
| ∃x 与 ∧ 搭配 | ∃x (H(x) ∧ ¬M(x)) | “存在一个…(是人且…)” |
| 合式公式 | 符合语法规则的表达式 | “逻辑中的合法句子” |
| 自由变元 | 不被任何量词约束的变量 | “未填的空格” |
| 约束变元 | 被量词绑定的变量 | “哑变量,可以换名” |
| 辖域 | 量词后面紧跟的子公式 | “量词的管辖范围” |
| 闭公式 | 没有自由变元的公式 | “完整的命题,有确定真假” |
| 解释 | 论域 + 常元指派 + 谓词含义 | “给公式填表——告诉每个符号代表什么” |
| 量词否定 | ¬∀x P(x) ⟺ ∃x ¬P(x) | “否定穿过量词,∀ 和 ∃ 互换” |
| ¬∃x P(x) ⟺ ∀x ¬P(x) | 同上 | “不存在…” = “所有…都不…” |
| 辖域扩张 | ∀x (A(x) ∧ B) ⟺ ∀x A(x) ∧ B | B 不含 x 时,B 可以提到量词外 |
| ∀ 对 ∧ 分配 | ∀x (A∧B) ⟺ ∀x A ∧ ∀x B | 成立 |
| ∃ 对 ∨ 分配 | ∃x (A∨B) ⟺ ∃x A ∨ ∃x B | 成立 |
| ∀ 对 ∨ 分配 | — | 不成立,“每人唱歌或跳舞” ≠ “所有人唱歌 或 所有人跳舞” |
| ∃ 对 ∧ 分配 | — | 不成立,“有人唱歌且跳舞” ≠ “有人唱歌 且 有人跳舞” |
| 前束范式 | Q₁x₁ Q₂x₂ … Qₙxₙ B(B 不含量词) | “量词全部提到最前面” |
| 前束范化步骤 | 消去 →/↔ → 否定内移 → 换名 → 量词提出 | 四步走 |
| 蕴涵消去 | A → B ⟺ ¬A ∨ B | “把 → 消掉”(来自命题逻辑) |
| 量词换名 | ∀x P(x) ⟺ ∀y P(y) | 约束变元是哑变量,换名不改变含义 |
第4章 二元关系和函数
本章定位:用集合和逻辑的语言来描述"事物之间的联系"——这是离散数学的核心内容之一。
驱动问题
你的微信好友关系、微博关注关系、课程选课关系——这些"谁和谁有联系"的数据,怎么用数学语言精确描述?
关系数据库(MySQL、PostgreSQL)的"表"就是数学上的关系。一张选课表:
| 学号 | 课程 |
|---|---|
| 001 | 数学 |
| 001 | 物理 |
| 002 | 数学 |
这就是集合 {(001, 数学), (001, 物理), (002, 数学)}——一个二元关系。
更深一层:为什么"模3同余"能把整数分成三类 {余0, 余1, 余2}?这背后的数学结构叫等价关系。为什么编译器能判断代码中变量的依赖顺序?因为依赖关系是一个偏序关系。
本章从"有序对"这个最基础的概念出发,逐步建立关系的完整理论,最后引入函数——一种特殊而重要的关系。
4.1 笛卡尔积与二元关系
为什么需要"有序对"?
在日常生活中,很多信息天然带有顺序:坐标 (3, 5) 和 (5, 3) 表示不同的位置;成绩单上 (张三, 90分) 和 (90分, 张三) 虽然包含同样的信息,但格式不同。我们需要一个数学工具来表达"有顺序的一对东西"。
概念讲解
有序对(Ordered Pair):由两个元素按特定顺序组成的对,记作 (a, b)。其中 a 叫第一分量(或首元素),b 叫第二分量(或尾元素)。
有序对的关键性质是:顺序决定相等。两个有序对 (a, b) 和 (c, d) 相等,当且仅当 a = c 且 b = d。
直觉类比:集合就像一个袋子——{苹果, 香蕉} = {香蕉, 苹果},装的东西一样就是同一个袋子。有序对就像一个有左右口袋的马甲——左边放苹果、右边放香蕉,和左边放香蕉、右边放苹果,是不同的穿法。
1 | {1, 2} = {2, 1} ← 集合:顺序无关 |
核心工具:笛卡尔积
有了有序对,我们自然想问:给定两个集合 A 和 B,所有可能的有序对构成什么?
笛卡尔积(Cartesian Product):A × B 是由 A 中元素作第一分量、B 中元素作第二分量的所有有序对构成的集合。形式化地:
1 | A × B = {(a, b) | a ∈ A 且 b ∈ B} |
直觉类比:笛卡尔积就像"全排列配对"。想象你有两堆卡片,A堆有 m 张,B堆有 n 张。从A堆取一张放在左边,从B堆取一张放在右边,所有可能的组合就是 A × B。
具体例子:
1 | A = {1, 2}, B = {x, y} |
重要:A × B ≠ B × A。笛卡尔积不满足交换律。这是初学者最常犯的错误之一——下意识地认为"乘法可以交换"。
元素个数:如果 |A| = m(A有m个元素),|B| = n(B有n个元素),那么 |A × B| = m × n。理由很简单:第一分量有 m 种选择,第二分量有 n 种选择,根据乘法原理(做一件事分两步,第一步有 m 种方法,第二步有 n 种方法,总共 m×n 种方法),总共有 mn 个有序对。
特殊情况:
1 | A × ∅ = ∅ ← 空集和任何集合的笛卡尔积都是空集 |
核心工具:二元关系
现在我们可以精确地定义"关系"了。
直觉类比:在微信里,“好友关系"就是一些人名对——(张三, 李四) 表示张三是李四的好友。所有好友对的集合,就是一个"关系”。
二元关系(Binary Relation):设 A 和 B 是两个集合,A 到 B 的一个二元关系 R 是 A × B 的一个子集,即 R ⊆ A × B。
如果 (a, b) ∈ R,我们记作 aRb,读作"a 与 b 有关系 R"。
1 | 设 A = {1, 2, 3},定义关系 R = "小于" |
关键理解:关系就是笛卡尔积的一个子集。不是所有有序对都属于关系——只有满足某种条件的有序对才在关系中。选课表不是所有(学生, 课程)对,只有实际选了课的那些对。
A 上的关系:当 A = B 时,R ⊆ A × A 称为 A 上的关系(或 A 中的关系)。这是最常见的情况。
1 | A = {张三, 李四, 王五} |
约定与记号
| 记号 | 含义 |
|---|---|
| (a, b) | 有序对,a 是第一分量,b 是第二分量 |
| A × B | A 和 B 的笛卡尔积 |
| R ⊆ A × B | R 是 A 到 B 的二元关系 |
| aRb | (a, b) ∈ R 的另一种写法 |
| dom® | R 的定义域 = {a | 存在 b 使得 (a,b) ∈ R},即所有出现在第一分量的元素 |
| ran® | R 的值域 = {b | 存在 a 使得 (a,b) ∈ R},即所有出现在第二分量的元素 |
4.2 关系的表示与运算
为什么需要"表示"?
关系 R 作为集合,可以用列出元素的方式表示。但当集合很大时,这种写法既占空间又不直观。我们需要更紧凑、更便于计算的表示方法——矩阵和图。
概念讲解:什么是矩阵?
在介绍关系矩阵之前,先简要回顾矩阵的概念。
矩阵(Matrix):一个 m×n 的矩阵是一个由 m 行 n 列数字排列成的矩形阵列。例如:
1 | A = [1 2 3] ← 这是一个 2×3 的矩阵(2行3列) |
矩阵中的每个数叫元素,用下标标记位置。A[i][j] 表示第 i 行、第 j 列的元素。例如上面的矩阵中,A[1][2] = 2(第1行第2列),A[2][1] = 4(第2行第1列)。
直觉类比:矩阵就像一个Excel表格——有行有列,每个格子里放一个值。
核心工具:关系矩阵
设 A = {a₁, a₂, …, aₙ} 是一个有限集合(n 个元素),R 是 A 上的关系。关系矩阵 M_R 是一个 n×n 的 0-1 矩阵(每个元素只能是 0 或 1),定义为:
1 | M_R[i][j] = 1 若 (aᵢ, aⱼ) ∈ R |
直觉类比:关系矩阵就像一张"是否"表格。行代表"谁",列代表"和谁",1 表示"有关系",0 表示"没关系"。
具体例子——逐步计算:
设 A = {1, 2, 3},R = {(1,1), (1,2), (2,3)}。
第一步:确定矩阵大小。A 有 3 个元素,所以矩阵是 3×3。
第二步:为每个位置填值。我们按行逐个检查:
1 | 第1行(对应元素1): |
最终得到关系矩阵:
1 | M_R = [1 1 0] ← 第1行:元素1与1有关系,与2有关系,与3没关系 |
从矩阵还原关系:如果给你一个关系矩阵,怎么还原关系?很简单——看哪些位置是 1。上面的矩阵中,位置 (1,1), (1,2), (2,3) 是 1,所以 R = {(1,1), (1,2), (2,3)}。
核心工具:关系图
另一种直观的表示方法是关系图(Digraph,有向图)。
直觉类比:关系图就像一张地铁线路图——站点是元素,箭头表示"从哪到哪"。
用点(圆圈)表示 A 中的每个元素,用有向边(带箭头的线段)表示关系:如果 (a, b) ∈ R,就从 a 画一个箭头指向 b。
自环:如果 (a, a) ∈ R(元素和自己有关系),就在 a 上画一个圈——箭头从 a 出发又回到 a。
1 | R = {(1,1), (1,2), (2,3)} |
解释:
- 从 1 到 1 有一个自环(因为 (1,1) ∈ R)
- 从 1 到 2 有一个箭头(因为 (1,2) ∈ R)
- 从 2 到 3 有一个箭头(因为 (2,3) ∈ R)
- 没有其他箭头
核心工具:关系的运算
关系作为集合,自然可以进行集合运算(并、交、差、补)。但关系还有两种特殊的运算:复合和逆。
复合关系
动机:在生活中,我们经常说"朋友的朋友"——先通过"朋友"关系找到一个人,再通过这个人的"朋友"关系找到另一个人。这种"先走一步关系,再走一步关系"的操作,就是复合。
复合关系(Composition):设 R₁ ⊆ A × B,R₂ ⊆ B × C。R₁ 和 R₂ 的复合 R₂ ∘ R₁ ⊆ A × C 定义为:
1 | (a, c) ∈ R₂ ∘ R₁ 当且仅当 存在某个 b ∈ B,使得 (a, b) ∈ R₁ 且 (b, c) ∈ R₂ |
直觉类比:复合就像"转车"。R₁ 是从 A 站到 B 站的路线,R₂ 是从 B 站到 C 站的路线。复合 R₂ ∘ R₁ 就是"从 A 到 B 再到 C"的路线。你需要在 B 站有一个"中转站"(即存在 b ∈ B)。
注意顺序:R₂ ∘ R₁ 表示"先走 R₁,再走 R₂"。这个顺序和函数复合 f(g(x)) 的习惯相反——在关系的复合中,R₂ ∘ R₁ 是从右往左读的。
具体例子:
1 | A = {1, 2}, B = {a, b, c}, C = {x, y} |
逆关系
动机:如果 R 表示"是…的父亲",那么反过来,R⁻¹ 表示"是…的儿子/女儿"。
逆关系(Inverse):设 R ⊆ A × B,R 的逆关系 R⁻¹ ⊆ B × A 定义为:
1 | (b, a) ∈ R⁻¹ 当且仅当 (a, b) ∈ R |
直觉类比:逆关系就是把所有箭头反向。关系图中,原来从 a 指向 b 的箭头,现在从 b 指向 a。
1 | R = {(1,a), (2,b), (3,a)} |
矩阵表示:逆关系的矩阵就是原矩阵的转置(行变列、列变行)。
1 | M_R = [1 0] M_{R⁻¹} = [1 1] ← 第1行和第2行交换变成列 |
应用场景
关系数据库:SQL 中的
JOIN操作本质上就是关系的复合。SELECT * FROM A JOIN B ON A.id = B.aid就是在求 A 和 B 在 id/aid 上的复合关系。ORDER BY DESC可以理解为在逆关系上操作。
4.3 关系的性质
为什么需要"性质"?
不同的关系有不同的"行为模式"。微信好友关系是双向的(对称的),但微博关注关系是单向的(非对称的)。“小于等于"允许"等于”(自反的),但"严格小于"不允许(反自反的)。这些行为模式可以用五个性质来精确描述。
为什么只讨论 A 上的关系? 因为这些性质(自反、对称、传递等)要求 (a, a)、(b, a)、(a, c) 这些有序对也在关系中。只有当关系 R ⊆ A × A(定义域和值域是同一个集合)时,这些讨论才有意义。
性质一:自反性
动机:在"小于等于"关系中,每个数都"小于等于"自己(1 ≤ 1,2 ≤ 2,…)。但在"严格小于"关系中,没有数"严格小于"自己。自反性描述的就是"是否每个元素都和自己有关系"。
自反性(Reflexivity):设 R 是集合 A 上的关系。如果对 A 中的每一个元素 a,都有 (a, a) ∈ R,则称 R 是自反的。
形式化地:R 是自反的 ⟺ ∀a ∈ A, (a, a) ∈ R
直觉类比:自反性就像"每个人都是自己的同学"——在一个班级里,你和你自己当然在同一个班级。
怎么判断?
- 集合表示:逐一检查——对 A 中每个 a,看 (a,a) 是否在 R 中。如果每一个都在,就是自反的。
- 矩阵特征:主对角线(从左上到右下的那条线)上的元素全部为 1。
- 图特征:每个顶点都有一个自环(箭头从自己指向自己)。
具体例子:
1 | A = {1, 2, 3} |
反例:
1 | A = {1, 2, 3} |
性质二:反自反性
动机:在"严格小于"关系中,没有任何元素小于自己。这种"所有元素都不和自己有关系"的性质,就是反自反性。
反自反性(Irreflexivity):设 R 是集合 A 上的关系。如果对 A 中的每一个元素 a,都有 (a, a) ∉ R,则称 R 是反自反的。
形式化地:R 是反自反的 ⟺ ∀a ∈ A, (a, a) ∉ R
直觉类比:反自反性就像"没有人是自己的敌人"——在敌对关系中,你不会和自己敌对。
怎么判断?
- 集合表示:逐一检查——对 A 中每个 a,看 (a,a) 是否不在 R 中。如果每一个都不在,就是反自反的。
- 矩阵特征:主对角线上的元素全部为 0。
- 图特征:没有任何自环。
具体例子:
1 | A = {1, 2, 3} |
容易混淆:自反性和反自反性不是非此即彼的关系。一个关系可以既不是自反的,也不是反自反的。例如 A = {1, 2},R = {(1,1)}——(1,1) 在但 (2,2) 不在,所以既不自反也不反自反。
性质三:对称性
动机:微信好友关系是双向的——如果张三是李四的好友,那么李四也是张三的好友。但微博关注是单向的——张三关注李四,不代表李四关注张三。对称性描述的就是这种"双向性"。
对称性(Symmetry):设 R 是集合 A 上的关系。如果对任意 a, b ∈ A,只要 (a, b) ∈ R,就一定有 (b, a) ∈ R,则称 R 是对称的。
形式化地:R 是对称的 ⟺ ∀a, b ∈ A, (a, b) ∈ R → (b, a) ∈ R
直觉类比:对称性就像"如果你认识我,我就认识你"——关系是双向的。
怎么判断?
- 集合表示:对 R 中每个有序对 (a, b),检查 (b, a) 是否也在 R 中。
- 矩阵特征:矩阵关于主对角线对称——M[i][j] = M[j][i]。
- 图特征:如果有一条从 a 到 b 的箭头,就一定有从 b 到 a 的箭头(边双向成对出现)。
具体例子:
1 | A = {1, 2, 3} |
反例:
1 | A = {1, 2, 3} |
性质四:反对称性
动机:在"小于等于"关系中,如果 a ≤ b 且 b ≤ a,那么 a = b。这不像"好友"关系那样双向——它是一种"不能同时双向,除非是同一个元素"的约束。
反对称性(Antisymmetry):设 R 是集合 A 上的关系。如果对任意 a, b ∈ A,只要 (a, b) ∈ R 且 (b, a) ∈ R,就一定有 a = b,则称 R 是反对称的。
形式化地:R 是反对称的 ⟺ ∀a, b ∈ A, [(a, b) ∈ R ∧ (b, a) ∈ R] → a = b
等价地(逆否命题):如果 a ≠ b 且 (a, b) ∈ R,则 (b, a) ∉ R。
直觉类比:反对称性就像"单行道"——如果 a 到 b 有路,那么 b 到 a 就不能有路(除非 a 和 b 是同一个地方)。
怎么判断?
- 集合表示:对 R 中每一对不同的元素 a ≠ b,如果 (a,b) ∈ R,检查 (b,a) 是否不在 R 中。
- 矩阵特征:关于主对角线对称的位置上,不能同时为 1(对角线上的元素不受限制)。
- 图特征:两个不同顶点之间最多只有一条有向边(不能有双向边)。
具体例子:
1 | A = {1, 2, 3} |
反例:
1 | A = {1, 2, 3} |
最容易混淆的概念:反对称 ≠ 不对称。
- 反对称:允许 (a,a),但不同元素之间不能双向。
- 不对称(不在五大性质中):(a,b) ∈ R → (b,a) ∉ R,连 (a,a) 也不允许。
- 不对称 = 反自反 + 反对称。
性质五:传递性
动机:如果张三是李四的朋友,李四是王五的朋友,那么在"朋友的朋友也是朋友"的规则下,张三和王五也应该有关系。但"父子"关系不传递——张三是李四的父亲,李四是王五的父亲,不代表张三是王五的父亲。传递性描述的是"间接关系是否能直接化"。
传递性(Transitivity):设 R 是集合 A 上的关系。如果对任意 a, b, c ∈ A,只要 (a, b) ∈ R 且 (b, c) ∈ R,就一定有 (a, c) ∈ R,则称 R 是传递的。
形式化地:R 是传递的 ⟺ ∀a, b, c ∈ A, [(a, b) ∈ R ∧ (b, c) ∈ R] → (a, c) ∈ R
直觉类比:传递性就像"接力传递"——如果 a 能传给 b,b 能传给 c,那么 a 就能传给 c。中间人 b 可以被"跳过"。
怎么判断?
- 集合表示:对 R 中所有满足 (a,b) ∈ R 且 (b,c) ∈ R 的组合,检查 (a,c) 是否也在 R 中。如果每一个这样的组合都有 (a,c) ∈ R,就是传递的。
- 矩阵特征:计算 M²(矩阵的平方——行乘以列),M² 中为 1 的位置在 M 中也必须为 1。
- 图特征:如果有 a→b 和 b→c 的路径,就一定有 a→c 的直接箭头。
具体例子:
1 | A = {1, 2, 3} |
反例:
1 | A = {1, 2, 3} |
总结对比
| 性质 | 通俗描述 | 矩阵特征 | 图特征 |
|---|---|---|---|
| 自反性 | 每个元素和自己有关系 | 对角线全 1 | 每个点有自环 |
| 反自反性 | 每个元素和自己没关系 | 对角线全 0 | 没有自环 |
| 对称性 | 有 a→b 就有 b→a | 矩阵关于对角线对称 | 边双向成对 |
| 反对称性 | a→b 且 b→a → a=b | 对称位置不同时为 1 | 无双向边(自环除外) |
| 传递性 | 有 a→b, b→c 就有 a→c | M² 的 1 在 M 中也是 1 | 有路径就有直连 |
4.4 闭包
为什么需要"闭包"?
给定一个关系 R,它可能不满足我们想要的性质。比如 R = {(1,2), (2,3)} 不是自反的(缺少 (1,1), (2,2), (3,3)),不是对称的(有 (1,2) 但没有 (2,1)),也不是传递的(有 (1,2) 和 (2,3) 但没有 (1,3))。
闭包(Closure)的思想是:在 R 的基础上,添加最少的有序对,使它具有我们想要的某个性质。
直觉类比:闭包就像"修补"——你的自行车少了一个铃铛(缺少某个性质),闭包就是只加一个铃铛,不多不少,刚好让它"完整"。
核心工具:三种闭包
自反闭包
自反闭包(Reflexive Closure):在 R 中添加所有 (a, a),使它变成自反的。
记作 r® = R ∪ I_A,其中 I_A = {(a, a) | a ∈ A} 称为 A 上的恒等关系(或对角线关系)。
1 | A = {1, 2, 3} |
对称闭包
对称闭包(Symmetric Closure):在 R 中添加所有逆关系中的对,使它变成对称的。
记作 s® = R ∪ R⁻¹。
1 | A = {1, 2, 3} |
传递闭包
传递闭包(Transitive Closure):在 R 中添加所有"间接可达"的有序对,使它变成传递的。
记作 t® = R ∪ R² ∪ R³ ∪ …,其中 R² = R ∘ R(复合一次),R³ = R ∘ R ∘ R(复合两次),以此类推。
直觉类比:传递闭包就是"把所有间接关系都补上"。如果 a 能通过一条路径到达 b,那么在传递闭包中,(a, b) 就应该存在。传递闭包就是"所有可达关系"。
1 | A = {1, 2, 3} |
核心工具:Warshall 算法
直接计算 R, R², R³, … 直到稳定,效率很低。Warshall 算法是计算传递闭包的高效方法,它用矩阵运算一步到位。
算法思想:
直觉类比:想象你要修路。一开始只有直接的路(原始关系 R)。然后你逐个开放"中转站":先允许经过节点 1 中转,看能不能连通更多地方;再允许经过节点 1 和 2 中转,看还能不能连通更多地方;…直到所有节点都可以作为中转站。
算法步骤(设集合 A 有 n 个元素 a₁, a₂, …, aₙ):
1 | W = M_R(从关系矩阵开始) |
这里的 ∨ 是逻辑或(有一个为 1 结果就是 1),∧ 是逻辑与(两个都为 1 结果才是 1)。
公式的含义:W[i][j] 更新为"原来 i 能否到达 j" 或者 “i 能否经过 k 中转到达 j”(即 W[i][k] = 1 且 W[k][j] = 1)。
完整小例子——逐步计算:
设 A = {1, 2, 3, 4},R = {(1,2), (2,3), (3,4)}。求传递闭包。
第一步:写出初始关系矩阵
1 | 1 2 3 4 |
第二步:k=1(允许经过节点1中转)
检查所有 (i, j):W[i][1] ∧ W[1][j] 是否为 1?
- W[1][1] ∧ W[1][j]:W[1][1] = 0,所以 W[i][1] ∧ W[1][j] 对所有 i 都是 0。
- 结论:没有新连接。矩阵不变。
1 | W = [0 1 0 0] |
第三步:k=2(允许经过节点1,2中转)
检查所有 (i, j):W[i][2] ∧ W[2][j] 是否为 1?
- i=1, j=3:W[1][2] = 1, W[2][3] = 1 → 1 ∧ 1 = 1 → W[1][3] = 0 ∨ 1 = 1(新增!)
- i=1, j=4:W[1][2] = 1, W[2][4] = 0 → 不变
- 其他组合:W[i][2] = 0(i=2 时 W[2][2]=0,i=3 时 W[3][2]=0,i=4 时 W[4][2]=0),所以不产生新连接。
1 | W = [0 1 1 0] ← W[1][3] 从 0 变成 1! |
解释:节点 1 能到 2(W[1][2]=1),节点 2 能到 3(W[2][3]=1),所以经过 2 中转,1 能到 3。
第四步:k=3(允许经过节点1,2,3中转)
检查所有 (i, j):W[i][3] ∧ W[3][j] 是否为 1?
- i=1, j=4:W[1][3] = 1, W[3][4] = 1 → 1 ∧ 1 = 1 → W[1][4] = 0 ∨ 1 = 1(新增!)
- i=2, j=4:W[2][3] = 1, W[3][4] = 1 → 1 ∧ 1 = 1 → W[2][4] = 0 ∨ 1 = 1(新增!)
- 其他组合不产生新连接。
1 | W = [0 1 1 1] ← W[1][4] 从 0 变成 1 |
解释:1 能到 3(上一步已知),3 能到 4,所以 1 能到 4。同理 2 能到 3,3 能到 4,所以 2 能到 4。
第五步:k=4(允许经过所有节点中转)
检查所有 (i, j):W[i][4] ∧ W[4][j] 是否为 1?
- W[4][j] 对所有 j 都是 0(节点 4 哪也到不了),所以不产生新连接。
1 | W = [0 1 1 1] ← 无变化 |
最终结果:
传递闭包 t® = {(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)}
验证:从 1 可达 2, 3, 4;从 2 可达 3, 4;从 3 可达 4。这正是链式关系 1→2→3→4 的传递闭包——所有"间接可达"的关系都被补上了。
4.5 等价关系与偏序关系
为什么需要"等价关系"和"偏序关系"?
在所有可能的关系中,有两类关系特别重要,因为它们在数学和计算机科学中无处不在。等价关系描述"分类"——把事物分成等价的组。偏序关系描述"排序"——在事物之间建立大小或先后。
概念讲解:等价关系
等价关系(Equivalence Relation):集合 A 上的关系 R 如果同时满足自反性、对称性、传递性三个性质,则称 R 是一个等价关系。
直觉类比:等价关系就是一种"分类标准"。它把集合中的元素分成若干组,每组内的元素"互相等价",不同组的元素"不等价"。
为什么这三条性质组合起来能表示"分类"?
- 自反性:每个元素和自己同类 → 每个元素至少属于一组
- 对称性:如果 a 和 b 同类,那么 b 和 a 同类 → "同类"是双向的
- 传递性:如果 a 和 b 同类,b 和 c 同类,那么 a 和 c 同类 → 同类关系可以传递
这三条性质合在一起,就精确地定义了"把集合分成不重叠的组"的操作。
经典例子——模3同余:
为什么整数除以 3 的余数能把整数分成三类?
定义关系 R:a R b ⟺ a 和 b 除以 3 的余数相同(即 3 | (a - b),读作"3 整除 a-b")。
1 | 验证 R 是等价关系: |
等价类划分:
1 | [0] = {..., -6, -3, 0, 3, 6, 9, ...} ← 除以3余0的数 |
核心工具:等价类与商集
等价类(Equivalence Class):设 R 是 A 上的等价关系,a ∈ A。a 的等价类 [a] 是所有与 a 等价的元素的集合:
1 | [a] = {x ∈ A | (x, a) ∈ R} ← 有些教材写 (a, x) ∈ R,两者等价(因为对称性) |
直觉类比:等价类就是"和 a 同一组的所有元素"。在模3同余中,[0] 就是"和 0 同余的所有数"。
等价类的重要性质:
- 非空:[a] ≠ ∅,因为 a ∈ [a](自反性保证了 (a,a) ∈ R)
- 覆盖全集:所有等价类的并集等于 A
- 互不相交:如果 [a] ≠ [b],那么 [a] ∩ [b] = ∅(没有元素同时属于两个不同的等价类)
- 等价类相等或不相交:对任意 a, b ∈ A,要么 [a] = [b],要么 [a] ∩ [b] = ∅
商集(Quotient Set):A 关于 R 的商集 A/R 是所有等价类构成的集合:
1 | A/R = {[a] | a ∈ A} |
1 | A = {1, 2, 3, 4, 5, 6} |
核心工具:划分
划分(Partition):集合 A 的一个划分是 A 的一组非空子集,满足:
- 每个子集非空
- 所有子集的并集等于 A
- 任意两个不同子集的交集为空
直觉类比:划分就像把一块蛋糕切成若干片——每片非空,所有片合起来是整块蛋糕,任意两片不重叠。
等价关系和划分的对应关系:
核心定理:等价关系和划分是一一对应的。
- 给定一个等价关系 R,可以得到一个划分 {等价类们}
- 给定一个划分,可以定义一个等价关系:两个元素等价当且仅当它们在同一块中
1 | 划分 {{1, 4}, {2, 5}, {3, 6}} 对应的等价关系: |
概念讲解:偏序关系
偏序关系(Partial Order):集合 A 上的关系 R 如果同时满足自反性、反对称性、传递性三个性质,则称 R 是一个偏序关系(或偏序)。集合 A 与偏序关系 R 一起构成的二元组 (A, R) 称为偏序集(Poset)。
直觉类比:偏序关系描述了一种"不完全的排序"。有些元素可以比较大小(谁大谁小),有些不能。"小于等于"对所有数都能比较,但"整除"关系中,2 和 3 谁也整除不了谁——它们不可比较。
经典例子:
| 偏序关系 | 集合 | 自反 | 反对称 | 传递 |
|---|---|---|---|---|
| 小于等于 (≤) | 实数 | a ≤ a ✓ | a ≤ b 且 b ≤ a → a=b ✓ | a ≤ b 且 b ≤ c → a ≤ c ✓ |
| 整除 (|) | 正整数 | a | a ✓ | a|b 且 b|a → a=b ✓ | a|b 且 b|c → a|c ✓ |
| 子集 (⊆) | 集合族 | A ⊆ A ✓ | A⊆B 且 B⊆A → A=B ✓ | A⊆B 且 B⊆C → A⊆C ✓ |
核心工具:哈斯图
偏序关系的关系图包含大量冗余信息——自反性意味着每个点都有自环,传递性意味着很多边是"多余的"(可以通过其他边间接得到)。哈斯图(Hasse Diagram)是偏序关系的简化表示。
画哈斯图的规则:
- 去掉自环:自反性已经隐含了,不需要画
- 去掉传递性隐含的边:如果有 a→b 和 b→c,就不要画 a→c(因为传递性保证了它的存在)
- 隐去箭头,约定向上:所有边不画箭头,约定下方的元素"小于"上方的元素
- 去掉自环边上的环
直觉类比:哈斯图就像一个"层级图"——越大的元素在越上面,只画"直接的"大小关系(相邻的),不画间接的。
怎么画?具体步骤:
以偏序集 ({1, 2, 3, 4, 6, 12}, |)(整除关系)为例。
第一步:列出所有关系
1 | 1|1, 1|2, 1|3, 1|4, 1|6, 1|12 |
第二步:去掉自环(去掉 a|a 的情况)
1 | 1|2, 1|3, 1|4, 1|6, 1|12 |
第三步:去掉传递性隐含的边
- 1|2 和 2|4 → 1|4 是传递的,去掉 1|4
- 1|2 和 2|6 → 1|6 是传递的,去掉 1|6
- 1|2 和 2|12 → 1|12 是传递的,去掉 1|12
- 1|3 和 3|6 → 1|6 已去掉
- 1|3 和 3|12 → 1|12 已去掉
- 2|4 和 4|12 → 2|12 是传递的,去掉 2|12
- 2|6 和 6|12 → 2|12 已去掉
- 3|6 和 6|12 → 3|12 是传递的,去掉 3|12
剩余的边(只画这些):
1 | 1|2, 1|3, 2|4, 2|6, 3|6, 4|12, 6|12 |
第四步:画图(小元素在下,大元素在上,无箭头)
1 | 12 |
更规范的画法:
1 | 12 |
解释:
- 1 在最下面,因为它整除所有数
- 12 在最上面,因为所有数都整除它
- 2 和 3 在同一层(都从 1 出发),但它们之间没有边(2 不整除 3,3 不整除 2)——它们是不可比较的
- 4 和 6 也不可比较(4 不整除 6,6 不整除 4)
概念讲解:格
格(Lattice):一个偏序集 (A, ≤) 如果满足:对 A 中任意两个元素 a 和 b,
- 都存在最小上界(Least Upper Bound, LUB),记作 a ∨ b(也叫"上确界"或"并")
- 都存在最大下界(Greatest Lower Bound, GLB),记作 a ∧ b(也叫"下确界"或"交")
则称 (A, ≤) 是一个格。
直觉类比:最小上界就是"刚好比 a 和 b 都大的最小元素"。最大下界就是"刚好比 a 和 b 都小的最大元素"。
具体例子:
在偏序集 ({1, 2, 3, 6}, |) 中:
1 | 6 |
- 2 和 3 的最小上界:比 2 和 3 都大的元素有 {6},最小的是 6。所以 2 ∨ 3 = 6。
- 2 和 3 的最大下界:比 2 和 3 都小的元素有 {1},最大的是 1。所以 2 ∧ 3 = 1。
- 2 和 6 的最小上界:比 2 和 6 都大的元素有 {6},最小的是 6。所以 2 ∨ 6 = 6。
- 2 和 6 的最大下界:比 2 和 6 都小的元素有 {1, 2},最大的是 2。所以 2 ∧ 6 = 2。
每对元素都有最小上界和最大下界 → 这是一个格。
应用场景
等价关系的应用:数据库中的 GROUP BY 操作就是基于等价关系——按某个字段分组,同一组的记录在这个字段上"等价"。编程语言中的类型推断也用到等价关系——两个类型等价就归为一组。
偏序关系的应用:文件系统的目录结构(子集关系)、任务调度的优先级(先后关系)、版本控制中的提交历史(都是偏序关系)。
4.6 函数
为什么函数是"特殊的关系"?
在日常理解中,函数是"输入一个值,输出一个值"的规则。但在数学上,函数有更精确的定义——它是一种特殊的关系。
直觉类比:普通的关系像一张表格,同一个输入可以对应多个输出(比如"选课关系"中,一个学生可以选多门课)。函数则是一个"规则"——每个输入恰好对应一个输出,不多不少。
概念讲解:函数的定义
函数(Function):设 A 和 B 是两个集合。一个从 A 到 B 的函数 f: A → B 是一个满足以下条件的二元关系 f ⊆ A × B:
- 存在性:对每个 a ∈ A,存在 b ∈ B 使得 (a, b) ∈ f(每个输入都有输出)
- 唯一性:对每个 a ∈ A,恰好有一个 b ∈ B 使得 (a, b) ∈ f(每个输入只有一个输出)
直觉类比:函数就像一台自动售货机——你投入一个硬币(输入),它一定会掉出一个商品(存在性),而且只掉出一个商品(唯一性)。如果你投了硬币什么都没出来,那就不是函数;如果掉出两个商品,也不是函数。
记号:如果 (a, b) ∈ f,我们通常写成 f(a) = b,读作"f 在 a 处的值等于 b"。
术语:
- A 称为 f 的定义域(Domain)
- B 称为 f 的陪域(Codomain)
- {f(a) | a ∈ A} 称为 f 的值域(Range),它是 B 的子集
哪些是函数,哪些不是?
1 | A = {1, 2, 3}, B = {a, b, c} |
核心工具:函数的三种分类
函数可以按"输出的分布方式"分为三类。
单射
单射(Injective / One-to-One):不同的输入对应不同的输出。形式化地:
1 | f 是单射 ⟺ 对任意 a₁, a₂ ∈ A,如果 f(a₁) = f(a₂),则 a₁ = a₂ |
等价地(逆否命题):如果 a₁ ≠ a₂,则 f(a₁) ≠ f(a₂)。
直觉类比:单射就像"没有两个人穿同一件衣服"——每个输出最多被一个输入使用。值域中的每个元素最多被"瞄准"一次。
1 | A = {1, 2, 3}, B = {a, b, c, d} |
满射
满射(Surjective / Onto):陪域中的每个元素都被映射到。形式化地:
1 | f 是满射 ⟺ 对任意 b ∈ B,存在 a ∈ A 使得 f(a) = b |
直觉类比:满射就像"每个座位都有人坐"——陪域中的每个元素至少被"瞄准"一次。
1 | A = {1, 2, 3}, B = {a, b} |
双射
双射(Bijective / One-to-One Correspondence):既是单射又是满射。形式化地:
1 | f 是双射 ⟺ f 是单射且 f 是满射 |
直觉类比:双射就像"完美配对"——每个输入对应一个唯一的输出,每个输出也被一个唯一的输入对应。就像两排人一一握手,不多不少。
1 | A = {1, 2, 3}, B = {a, b, c} |
三种函数的关系:
1 | 双射 = 单射 + 满射 |
核心工具:函数的复合
函数的复合和关系的复合是一回事,只是换了一种更常见的写法。
函数的复合(Composition):设 f: A → B,g: B → C。它们的复合 g ∘ f: A → C 定义为:
1 | (g ∘ f)(a) = g(f(a)) ← 先算 f(a),再算 g |
直觉类比:复合就像"流水线"——原料先经过 f 加工,再经过 g 加工。先 f 后 g。
具体例子:
1 | f: {1, 2, 3} → {a, b}, f(1)=a, f(2)=b, f(3)=a |
注意顺序:g ∘ f 表示"先 f 后 g"。一般来说 g ∘ f ≠ f ∘ g(甚至 f ∘ g 可能没有定义,如果 g 的值域不是 f 的定义域的子集的话)。
复合保持的性质:
| f | g | g ∘ f |
|---|---|---|
| 单射 | 单射 | 单射 |
| 满射 | 满射 | 满射 |
| 双射 | 双射 | 双射 |
核心工具:反函数
反函数(Inverse Function):设 f: A → B 是双射。则存在唯一的函数 f⁻¹: B → A,满足:
1 | f⁻¹(f(a)) = a 对所有 a ∈ A |
f⁻¹ 称为 f 的反函数(或逆函数)。
直觉类比:如果 f 是"把密码翻译成明文",那么 f⁻¹ 就是"把明文翻译回密码"。只有当翻译过程没有信息损失(双射)时,才能完美地反向翻译。
怎么求反函数?
如果 f = {(1,a), (2,b), (3,c)},那么 f⁻¹ = {(a,1), (b,2), (c,3)}——把每个有序对的两个元素交换位置。这恰好就是逆关系的定义!
关键:只有双射才有反函数。如果 f 不是单射,反函数无法确定"该回到哪个输入";如果 f 不是满射,某些输出没有对应的输入。
具体例子:
1 | f: ℝ → ℝ, f(x) = 2x + 1 |
例题与详解
例题 1:判断关系的性质
题目:设 A = {1, 2, 3},R = {(1,1), (2,2), (3,3), (1,2), (2,1)}。判断 R 是否具有自反性、对称性、传递性。
整体分析
这道题考查对关系性质定义的理解。策略是:逐一检查每个性质,按照定义逐条验证。注意自反性和对称性是"全局检查"(需要检查所有元素或所有对),传递性是"组合检查"(需要检查所有满足前提条件的组合)。
详解
自反性:需要 ∀a ∈ A, (a,a) ∈ R。
逐个检查:
- (1,1) ∈ R?在 ✓
- (2,2) ∈ R?在 ✓
- (3,3) ∈ R?在 ✓
三个都在 → R 是自反的。
对称性:需要对所有 (a,b) ∈ R,都有 (b,a) ∈ R。
逐一检查 R 中的每个有序对:
- (1,1) → (1,1) ∈ R ✓
- (2,2) → (2,2) ∈ R ✓
- (3,3) → (3,3) ∈ R ✓
- (1,2) → (2,1) ∈ R ✓(这是关键!如果有 (1,2) 但没有 (2,1),就不对称)
- (2,1) → (1,2) ∈ R ✓
每一对都有"反向对" → R 是对称的。
传递性:需要对所有满足 (a,b) ∈ R 且 (b,c) ∈ R 的组合,都有 (a,c) ∈ R。
策略:找出所有 (a,b) 和 (b,c) 的"接力"组合:
- (1,1) 和 (1,1) → 需要 (1,1) ∈ R ✓
- (1,1) 和 (1,2) → 需要 (1,2) ∈ R ✓
- (1,2) 和 (2,1) → 需要 (1,1) ∈ R ✓
- (1,2) 和 (2,2) → 需要 (1,2) ∈ R ✓
- (2,1) 和 (1,1) → 需要 (2,1) ∈ R ✓
- (2,1) 和 (1,2) → 需要 (2,2) ∈ R ✓
- (2,2) 和 (2,1) → 需要 (2,1) ∈ R ✓
- (2,2) 和 (2,2) → 需要 (2,2) ∈ R ✓
- (3,3) 和 (3,3) → 需要 (3,3) ∈ R ✓
所有组合都满足 → R 是传递的。
结论:R 同时满足自反性、对称性、传递性,因此 R 是一个等价关系。
验证直觉:这个等价关系把 A 分成了两个等价类:[1] = [2] = {1, 2}(1 和 2 等价),[3] = {3}(3 自成一类)。划分是 {{1,2}, {3}}。
例题 2:关系矩阵与复合关系
题目:设 A = {1, 2, 3},R₁ = {(1,2), (2,2), (3,1)},R₂ = {(1,3), (2,1), (3,2)}。求 R₂ ∘ R₁ 的关系矩阵。
整体分析
有两种方法:(1) 先写出关系矩阵,用矩阵乘法的思想;(2) 直接用集合定义逐个检查。这里两种方法都展示。
详解
方法一:矩阵法
第一步:写出 R₁ 和 R₂ 的关系矩阵。
1 | 1 2 3 1 2 3 |
第二步:复合关系的矩阵通过"逻辑矩阵乘法"得到。M_{R₂∘R₁}[i][j] = 1 当且仅当存在 k 使得 M₁[i][k] = 1 且 M₂[k][j] = 1。
计算每个位置:
1 | M[1][1]:存在 k 使得 M₁[1][k]=1 且 M₂[k][1]=1? |
结果:
1 | 1 2 3 |
R₂ ∘ R₁ = {(1,1), (2,1), (3,3)}
方法二:直接验证
从定义出发,(a, c) ∈ R₂ ∘ R₁ 当且仅当存在 b 使得 (a,b) ∈ R₁ 且 (b,c) ∈ R₂。
1 | a=1:R₁ 中 (1,2) ∈ R₁,b=2。R₂ 中 (2,1) ∈ R₂,c=1。→ (1,1) ∈ R₂ ∘ R₁ |
结果一致:R₂ ∘ R₁ = {(1,1), (2,1), (3,3)} ✓
例题 3:Warshall 算法
题目:设 A = {1, 2, 3},R = {(1,2), (2,1), (2,3)}。用 Warshall 算法求传递闭包。
整体分析
这个例子比链式关系更有趣——有循环(1↔2)和分支(2→3)。通过 Warshall 算法可以看到循环如何导致更多的可达关系。
详解
第一步:初始矩阵
1 | 1 2 3 |
第二步:k=1(允许经过节点1中转)
对所有 (i,j),检查 W[i][1] ∧ W[1][j]:
- i=2, j=2:W[2][1]=1, W[1][2]=1 → 1∧1=1 → W[2][2] = 0∨1 = 1
- i=2, j=3:W[2][1]=1, W[1][3]=0 → 不变
- 其他 i 的 W[i][1] = 0,不产生新连接
1 | W = [0 1 0] |
解释:2 能到 1(W[2][1]=1),1 能到 2(W[1][2]=1),所以经过 1 中转,2 能到 2(自己回到自己)。
第三步:k=2(允许经过节点1,2中转)
对所有 (i,j),检查 W[i][2] ∧ W[2][j]:
- i=1, j=1:W[1][2]=1, W[2][1]=1 → 1 → W[1][1] = 0∨1 = 1
- i=1, j=2:W[1][2]=1, W[2][2]=1 → 1 → W[1][2] 已经是 1
- i=1, j=3:W[1][2]=1, W[2][3]=1 → 1 → W[1][3] = 0∨1 = 1
- i=3:W[3][2]=0,不产生新连接
1 | W = [1 1 1] ← W[1][1] 和 W[1][3] 从 0 变成 1 |
解释:1 能到 2(W[1][2]=1),2 能到 1(W[2][1]=1),所以 1 能到 1。1 能到 2,2 能到 3(W[2][3]=1),所以 1 能到 3。
第四步:k=3(允许经过所有节点中转)
对所有 (i,j),检查 W[i][3] ∧ W[3][j]:
- W[3][j] 对所有 j 都是 0,不产生新连接。
1 | W = [1 1 1] ← 无变化 |
最终结果:
传递闭包 t® = {(1,1), (1,2), (1,3), (2,1), (2,2), (2,3)}
直觉验证:从 1 出发,经过 2 可以回到 1(循环),可以到 3。从 2 出发,经过 1 可以回到 2(循环),可以直接到 3。3 是"死胡同"——从 3 出发到不了任何地方。所以 1 和 2 都能到达 {1, 2, 3} 中的所有元素,3 哪也到不了。
例题 4:等价关系与划分
题目:设 A = {1, 2, 3, 4, 5, 6},R 是模 3 同余关系(即 aRb 当且仅当 a 和 b 除以 3 的余数相同)。求所有等价类和商集。
整体分析
模 3 同余是等价关系的经典例子。策略是:先确认它是等价关系(已在理论部分验证),然后按余数分组。
详解
对 A 中每个元素,计算它除以 3 的余数:
1 | 1 ÷ 3 = 0 余 1 → 余数 1 |
按余数分组:
1 | 余数 0:{3, 6} → 等价类 [3] = [6] = {3, 6} |
商集:A/R = {{3, 6}, {1, 4}, {2, 5}}
验证:
- 每个等价类非空 ✓
- 三个等价类的并集 = {3,6} ∪ {1,4} ∪ {2,5} = {1,2,3,4,5,6} = A ✓
- 任意两个等价类的交集为空 ✓
注意:[3] = [6],因为 3 和 6 在同一个等价类中。等价类中任何一个元素都可以作为代表——选 [3] 还是 [6] 来称呼这个等价类,结果是一样的。
例题 5:哈斯图的画法
题目:画出偏序集 ({2, 3, 4, 6, 12}, |) 的哈斯图。
整体分析
画哈斯图的关键是:先列出所有偏序关系,然后去掉自环和传递性隐含的边,最后把小元素画在下面。
详解
第一步:确定偏序关系
整除关系 a|b 意味着"b 能被 a 整除"(即 b/a 是整数)。列出所有有序对:
1 | 2|2, 2|4, 2|6, 2|12 |
去掉自环后:
1 | 2|4, 2|6, 2|12 |
第二步:去掉传递性隐含的边
- 2|4 和 4|12 → 2|12 是传递的,去掉 2|12
- 2|6 和 6|12 → 2|12(已去掉)
- 3|6 和 6|12 → 3|12 是传递的,去掉 3|12
剩余的边:
1 | 2|4, 2|6, 3|6, 4|12, 6|12 |
第三步:画图
从小到大排列元素,画出边:
1 | 12 |
更清晰的画法:
1 | 12 ← 最大,在最上面 |
解释每条边:
- 2 → 4:2|4(4÷2=2,整除)
- 2 → 6:2|6(6÷2=3,整除)
- 3 → 6:3|6(6÷3=2,整除)
- 4 → 12:4|12(12÷4=3,整除)
- 6 → 12:6|12(12÷6=2,整除)
没有画的边(为什么?):
- 2 和 3 之间没有边:2 不整除 3,3 不整除 2——不可比较
- 4 和 6 之间没有边:4 不整除 6,6 不整除 4——不可比较
- 2→12 没有画:因为 2→4→12 已经有了(传递性隐含)
例题 6:判断单射、满射、双射
题目:设 f: {1, 2, 3} → {a, b, c, d},f = {(1,a), (2,b), (3,c)}。判断 f 是否为单射、满射、双射。
整体分析
按照定义逐一检查:单射看"不同输入是否对应不同输出",满射看"陪域中每个元素是否都被映射到"。
详解
单射检查:看是否存在不同输入对应相同输出。
1 | f(1) = a, f(2) = b, f(3) = c |
满射检查:看陪域 {a, b, c, d} 中每个元素是否都被映射到。
1 | a 被 1 映射到 ✓ |
双射:需要同时是单射和满射。f 是单射但不是满射 → f 不是双射。
直觉:定义域有 3 个元素,陪域有 4 个元素。“3 个人分 4 个座位”——即使每个人坐不同的座位,也一定有一个座位空着(不满射)。
修改为满射:如果把陪域改为 {a, b, c}(去掉 d),那么 f: {1,2,3} → {a,b,c} 就是满射了。此时 f 是双射。
修改为不单射:如果 f = {(1,a), (2,b), (3,a)},那么 f(1) = f(3) = a,不同输入对应相同输出,f 不是单射。此时即使陪域是 {a, b},f 也不是双射。
本章速查表
| 概念 | 一句话解释 | 形式化定义 |
|---|---|---|
| 有序对 | 有顺序的两个元素组成的对 | (a, b),a 是第一分量,b 是第二分量 |
| 笛卡尔积 | 两个集合所有可能的有序对 | A × B = {(a,b) | a ∈ A, b ∈ B} |
| 二元关系 | 笛卡尔积的一个子集 | R ⊆ A × B |
| 关系矩阵 | 用 0-1 矩阵表示关系,1 表示有关系 | M[i][j] = 1 当且仅当 (aᵢ, aⱼ) ∈ R |
| 关系图 | 用有向边表示关系的图 | 点=元素,箭头=关系 |
| 复合关系 | 先走 R₁ 再走 R₂ | (a,c) ∈ R₂∘R₁ ⟺ ∃b: (a,b)∈R₁ ∧ (b,c)∈R₂ |
| 逆关系 | 所有箭头反向 | (b,a) ∈ R⁻¹ ⟺ (a,b) ∈ R |
| 自反性 | 每个元素和自己有关系 | ∀a, (a,a) ∈ R |
| 反自反性 | 每个元素和自己没关系 | ∀a, (a,a) ∉ R |
| 对称性 | 有 a→b 就有 b→a | (a,b) ∈ R → (b,a) ∈ R |
| 反对称性 | 不同元素之间不能双向 | [(a,b) ∈ R ∧ (b,a) ∈ R] → a = b |
| 传递性 | 有 a→b, b→c 就有 a→c | [(a,b) ∈ R ∧ (b,c) ∈ R] → (a,c) ∈ R |
| 自反闭包 | 补上所有 (a,a) | r® = R ∪ I_A |
| 对称闭包 | 补上所有逆对 | s® = R ∪ R⁻¹ |
| 传递闭包 | 补上所有间接可达的对 | t® = R ∪ R² ∪ R³ ∪ … |
| Warshall 算法 | 逐步开放中转站,计算传递闭包 | W[i][j] = W[i][j] ∨ (W[i][k] ∧ W[k][j]) |
| 等价关系 | 自反+对称+传递,用于分类 | R 是等价关系 ⟺ R 自反 ∧ 对称 ∧ 传递 |
| 等价类 | 与 a 等价的所有元素的集合 | [a] = {x | (x,a) ∈ R} |
| 商集 | 所有等价类构成的集合 | A/R = {[a] | a ∈ A} |
| 划分 | 把集合分成互不相交的非空子集 | 子集非空、不重叠、并集为全集 |
| 偏序关系 | 自反+反对称+传递,用于排序 | R 是偏序 ⟺ R 自反 ∧ 反对称 ∧ 传递 |
| 哈斯图 | 偏序关系的简化图 | 去掉自环、传递边、箭头(约定向上) |
| 格 | 任意两元素都有最小上界和最大下界的偏序集 | ∀a,b: a∨b 和 a∧b 都存在 |
| 函数 | 每个输入恰好对应一个输出的关系 | f ⊆ A × B,∀a ∈ A 存在唯一 b: (a,b) ∈ f |
| 单射 | 不同输入 → 不同输出 | f(a₁)=f(a₂) → a₁=a₂ |
| 满射 | 陪域中每个元素都被映射到 | ∀b ∈ B, ∃a ∈ A: f(a)=b |
| 双射 | 既是单射又是满射(一一对应) | 单射 ∧ 满射 |
| 函数复合 | 先 f 再 g | (g∘f)(a) = g(f(a)) |
| 反函数 | 双射的"逆操作" | f⁻¹(f(a)) = a, f(f⁻¹(b)) = b |
第5章 图
本章定位:关系的可视化——图是描述"谁和谁相连"最自然的工具。
驱动问题
在进入正式定义之前,先看四个真实问题,它们看似毫无关联,却都能用同一个数学工具来解决。
问题一:七桥问题——图论的诞生
1736年,东普鲁士的哥尼斯堡城(今俄罗斯加里宁格勒)有一条河穿过城区,河中有两个小岛,七座桥将两个小岛与两岸连接如下:
1 | 北岸 |
市民们发现一个有趣的挑战:能否从某处出发,恰好经过每座桥一次,最后回到起点? 他们试了无数次都没有成功。
欧拉的天才在于:他把问题抽象了——陆地变成点,桥变成连接点的线。他不需要关心陆地多大、桥多宽、河水多深,只需要关心"谁和谁相连"。这就是图论的诞生。
直觉类比:你第一次去一个陌生的商场,想找洗手间。你不会记住每条走廊的长度和宽度——你只关心"从这个电梯口往左转能到餐厅,往右转能到洗手间"。这就是"图"的本质:只保留连接关系,丢掉一切无关细节。
问题二:高德地图导航——最短路径
你打开高德地图,输入"从家到公司"。地图上有成千上万个路口和道路,系统怎么在不到一秒的时间内算出最快路线?这背后就是Dijkstra算法——一种从起点出发,逐步探索最近的路口,直到找到目的地的系统方法。
问题三:项目管理——关键路径
你负责一个软件项目,有几十个任务:需求分析、设计、编码、测试、部署……每个任务都有依赖关系(比如"编码"必须在"设计"完成之后才能开始)和耗时。问题是:整个项目最快多久能完成?哪些任务一天都不能延迟? 这就是关键路径法——它告诉你项目的"瓶颈"在哪里。
问题四:频率分配——图着色
一个蜂窝网络有20个基站,相邻基站不能使用相同的频率(否则会互相干扰)。最少需要准备多少种不同的频率?这个问题可以转化为:给每个基站分配一个"颜色"(频率),相邻基站颜色不同,最少需要几种颜色?这就是图着色问题。
5.1 无向图与有向图
为什么需要"图"?
在日常生活和科学研究中,我们经常需要描述事物之间的关系:
- 社交网络中,谁和谁是朋友?
- 交通网络中,哪些城市之间有直飞航班?
- 计算机网络中,哪些路由器直接相连?
- 课程安排中,哪些课之间有先修关系?
这些场景有一个共同特点:都有实体(人、城市、路由器、课程)和实体之间的关系(友谊、航线、连接、先修)。图就是描述这种"实体+关系"结构的数学工具。
直觉类比:想象你画一幅地铁线路图。地铁站用圆圈表示,两个站之间的线路用线段连接。这就是一个"图"——圆圈是顶点,线段是边。你不需要知道每个站有多大、线路有多长,只需要知道哪些站相连。
图的定义
图(Graph)是一个数学对象 G = (V, E),由两部分组成:
- V 是一个非空的有限集合,称为顶点集(也叫结点集)。集合中的每个元素叫做一个顶点(vertex,复数 vertices,也叫结点 node)。
- E 是一个有限集合,称为边集。集合中的每个元素叫做一条边(edge),每条边连接两个顶点。
记号:我们用 |V| 表示顶点的个数,|E| 表示边的个数。如果 G 是一个图,我们说"G 有 n 个顶点、m 条边"就是指 |V| = n,|E| = m。
直觉类比:顶点就像地图上的城市,边就像城市之间的公路。整个图就是一张"关系地图"。
无向图与有向图
无向图(Undirected Graph):边没有方向。一条边 {u, v} 表示顶点 u 和 v 之间存在一个双向的、对称的关系。
直觉类比:微信好友关系是无向的——如果你是我的好友,那我也是你的好友。我们用一条没有箭头的线连接两个人。
有向图(Directed Graph,简称 digraph):边有方向。一条边用有序对 (u, v) 表示,意思是"从 u 指向 v"——这是一个单向的关系。在图上,有向边画成带箭头的线。
直觉类比:微博关注关系是有向的——我可以关注你,但你可以不关注我。我们用一条从"关注者"指向"被关注者"的箭头表示。
区分记号:
- 无向边写成花括号 {u, v},因为 {u, v} = {v, u},顺序无所谓。
- 有向边写成圆括号 (u, v),因为 (u, v) ≠ (v, u),顺序很重要。
邻接与关联——两个重要术语
邻接(adjacent):如果两个顶点 u 和 v 之间有一条边连接,我们就说 u 和 v 是邻接的(或者说 u 是 v 的邻居)。
直觉类比:住在隔壁的邻居就是"邻接的"——你们之间有一面墙(边)相连。
关联(incident):如果一条边 e 的两个端点是 u 和 v,我们就说边 e 关联于顶点 u 和 v(或者说 e 与 u、v 相关联)。
直觉类比:你和你的家门牌号之间的关系——门牌号"关联"着你住的这栋楼。
一些特殊类型的边和顶点
- 自环(loop):一条边的两个端点是同一个顶点,即边 {v, v} 或 (v, v)。画出来就是一个顶点上有个小圈。
- 平行边(parallel edges,也叫多重边):两条或更多条边连接同一对顶点。
- 简单图(simple graph):没有自环、没有平行边的图。本章主要讨论简单图。
- 孤立顶点(isolated vertex):没有任何边与之关联的顶点,即度为 0 的顶点。
- 零图(null graph):只有孤立顶点、没有任何边的图。
度——衡量顶点的"繁忙程度"
度(Degree)是衡量一个顶点有多少条边与之关联的指标。
在无向图中,顶点 v 的度记作 deg(v),定义为与 v 关联的边的数量。特别地,自环对度的贡献为 2(因为它有两个端点都指向 v)。
直觉类比:一个人的"社交度"就是他的好友数量。好友越多,度越大,说明这个人社交越活跃。
在有向图中,一个顶点的度被进一步细分为两种:
- 入度(in-degree),记作 deg⁻(v):指向 v 的边的数量(有多少条边"进入"v)。可以理解为"有多少人关注了你"。
- 出度(out-degree),记作 deg⁺(v):从 v 出发的边的数量(有多少条边"离开"v)。可以理解为"你关注了多少人"。
在有向图中,一个顶点的总度 = 入度 + 出度,即 deg(v) = deg⁻(v) + deg⁺(v)。
握手定理——图论中最基本的定理
握手定理(Handshaking Theorem):在任何无向图 G = (V, E) 中,所有顶点的度之和等于边数的两倍:
证明思路(非常直观):
想象一个聚会,每两个人之间如果握了手,就在这两人之间连一条边。现在我们来数"总共有多少只手参与了握手"——有两种数法:
- 按人来数:每个人伸出的手数就是他的度,所有人伸出的手数之和就是 。
- 按握手事件来数:每次握手涉及两个人、两只手,所以总手数 = 2 × 握手次数 = 2|E|。
两种数法数的是同一件事,所以结果相等:。
重要推论:度为奇数的顶点的个数一定是偶数。
为什么? 度之和 是偶数。把所有顶点按度的奇偶分成两组:奇度顶点组和偶度顶点组。偶度顶点组的度之和一定是偶数(偶数加偶数还是偶数)。那么奇度顶点组的度之和也必须是偶数(因为总和是偶数减去一个偶数)。而奇数个奇数之和是奇数,偶数个奇数之和才是偶数——所以奇度顶点的个数必须是偶数。
直觉类比:在一个聚会上,"握了奇数次手的人"的数量一定是偶数。不可能出现恰好 3 个人各自握了奇数次手——因为奇数+奇数+奇数 = 奇数,不等于 2|E|(偶数)。
子图
子图(subgraph):给定图 G = (V, E),如果图 G’ = (V’, E’) 满足 V’ ⊆ V(V’ 是 V 的子集)且 E’ ⊆ E(E’ 是 E 的子集),并且 E’ 中的每条边的两个端点都在 V’ 中,那么 G’ 是 G 的子图。
直觉类比:如果 G 是一张完整的全国铁路图,那么只保留北京、上海、广州及其之间的线路,就得到 G 的一个子图。
生成子图(spanning subgraph):V’ = V 的子图——保留所有顶点,但可以去掉一些边。
直觉类比:生成子图就像全国铁路图的"精简版"——所有城市都还在,但有些线路被取消了。
导出子图(induced subgraph):选取 V 的一个子集 V’,保留所有两个端点都在 V’ 中的边。即 V’ ⊆ V,E’ = {{u,v} ∈ E | u ∈ V' 且 v ∈ V'}。
直觉类比:导出子图就像"只看华东地区的铁路图"——把华东的所有城市和它们之间的所有线路都保留下来。
完全图与二部图
完全图(complete graph):每对不同的顶点之间都恰好有一条边相连的简单图。n 个顶点的完全图记作 。
- 是一个三角形(3个顶点,3条边)。
- 是一个四边形加两条对角线(4个顶点,6条边)。
- 的边数为 。
二部图(bipartite graph,也叫偶图):顶点集可以分成两个不相交的子集 A 和 B,使得每条边都恰好连接 A 中的一个顶点和 B 中的一个顶点(A 内部没有边,B 内部也没有边)。
直觉类比:一个相亲活动,左边是男生,右边是女生,边表示"配对意向"。男生之间、女生之间不会有边——这就是二部图。
如果 A 中每个顶点都和 B 中每个顶点相连,就叫完全二部图,记作 ,其中 |A| = m,|B| = n。
5.2 通路、回路与连通性
为什么需要"通路"的概念?
想象你在一座陌生的城市里,想从酒店走到博物馆。你关心的不是具体的街道名字,而是"能不能走到"以及"怎么走"。通路就是描述"从一个顶点走到另一个顶点"的数学语言。
通路
通路(path):在一个图中,通路是一个交替的顶点和边的序列:
其中每条边 连接顶点 和 。这条通路从 出发,到 结束,长度为 k(边的数量)。
直觉类比:通路就像一串脚印——你从某个路口出发,沿着一条路走到下一个路口,再沿着另一条路走到再下一个路口……直到目的地。
通路的长度:通路中边的数量。注意不是顶点的数量。
简单通路(simple path):所有顶点都互不相同的通路(没有重复经过同一个顶点)。这意味着简单通路中也没有重复的边。
直觉类比:简单通路就像"不走回头路"的旅行——你不会两次经过同一个路口。
迹(trail):所有边都互不相同的通路(没有重复走同一条边),但顶点可以重复。
直觉类比:迹就是"不重复走同一条路"的旅行,但你可能绕了一圈又经过同一个路口。
总结一下层次关系:简单通路 ⊆ 迹 ⊆ 通路(简单通路是最严格的,通路是最宽松的)。
回路
回路(cycle,也叫圈):起点和终点相同的通路,即 。
简单回路(simple cycle,也叫基本回路):除了起点和终点可以相同外,其余顶点都互不相同的回路。
直觉类比:回路就是"出门走了一圈又回到家"。简单回路就是"不重复经过任何地方的环形路线"。
在无向图中:长度为 1 的回路是自环;长度为 2 的回路需要两条平行边(在简单图中不存在);长度为 3 的回路是一个三角形。
在有向图中:回路也叫有向回路,要求每条边的方向与行走方向一致。
连通性——图"连不连"?
连通(connected):在无向图中,如果对于任意两个顶点 u 和 v,都存在一条从 u 到 v 的通路,那么这个图是连通的。
直觉类比:一个连通的铁路网意味着你可以从任何一个城市坐火车到达任何另一个城市(可能需要换乘,但总能到达)。
不连通的图可以分成若干个"连在一起的部分",每个部分叫做一个连通分量。
连通分量(connected component):无向图的一个极大连通子图。"极大"的意思是:你不能再往里面加任何顶点和边而保持连通性。
直觉类比:想象一个群岛——每个岛上的城市之间有公路相连,但岛与岛之间没有桥。每个岛(及其上的所有公路)就是一个连通分量。
在有向图中,连通性更复杂,因为方向限制了移动:
- 强连通(strongly connected):对于任意两个顶点 u 和 v,都存在从 u 到 v 的有向通路 AND 从 v 到 u 的有向通路。也就是说,你可以从任何地方走到任何地方,并且还能走回来。
- 弱连通(weakly connected):如果把所有有向边的箭头去掉(变成无向图)后是连通的。
- 强连通分量:有向图中的极大连通子图。
直觉类比:强连通就像一个城市里的单行道系统——虽然很多路是单向的,但你总能绕一圈到达任何地方再回来。弱连通就像只考虑"有没有路",不管方向——只要把所有单行道都变成双行道后能到达就行。
5.3 图的矩阵表示
为什么用矩阵表示图?
计算机存储图的方式之一是用矩阵。矩阵表示的好处是:可以用成熟的线性代数工具(矩阵乘法、转置等)来分析图的性质。
邻接矩阵——谁和谁相邻?
邻接矩阵(adjacency matrix)是图的最直接的矩阵表示。给定图 G = (V, E),顶点集 V = {v₁, v₂, …, vₙ},邻接矩阵 A 是一个 n × n 的矩阵,定义如下:
无向图的邻接矩阵一定是对称的(A[i][j] = A[j][i]),因为无向边 {u, v} 意味着 u 邻接于 v 且 v 邻接于 u。
有向图的邻接矩阵不一定对称。A[i][j] = 1 表示存在从 指向 的有向边。
带权图的邻接矩阵:如果图的边有权重(比如距离、时间、费用),那么 A[i][j] 等于边的权重(如果边存在),否则为 0 或 ∞。
示例:考虑下面这个有向图:
1 | v₁ → v₂ |
边集:{(v₁,v₂), (v₁,v₃), (v₂,v₄), (v₃,v₄)}
邻接矩阵:
1 | v₁ v₂ v₃ v₄ |
怎么读这个矩阵?第 i 行第 j 列的元素表示"有没有从 到 的边"。例如 A[1][2] = 1 表示有从 到 的边。
邻接矩阵的幂——通路计数
邻接矩阵的幂有一个美妙的含义:设 A 是图 G 的邻接矩阵,则 的第 (i, j) 个元素等于从 到 恰好长度为 k 的通路的数量。
为什么?以 为例:。这个求和的意思是:遍历所有可能的"中间顶点" ,如果 到 有边(A[i][l] = 1)且 到 有边(A[l][j] = 1),那么 就是一条长度为 2 的通路。把所有可能的中间顶点加起来,就是长度为 2 的通路总数。
直觉类比: 告诉你"一步能到哪里", 告诉你"两步能到哪里(以及有多少种走法)“, 告诉你"三步能到哪里”……以此类推。
可达矩阵——能不能到达?
可达矩阵(reachability matrix)回答的问题是:从 能不能到达 (不管经过多少步)?
可达矩阵 P 也是一个 n × n 矩阵,定义如下:
如何计算可达矩阵?
方法一:利用邻接矩阵的幂。计算 (n 是顶点数),然后对它们取"布尔或"(只要某个 的 (i,j) 位置非零,P[i][j] 就是 1):
为什么只需要算到 ?因为如果存在从 到 的通路,那么一定存在一条长度不超过 n-1 的通路(否则通路中必然有重复顶点,可以去掉中间的回路得到更短的通路)。
方法二:使用 Warshall 算法(一种动态规划方法),时间复杂度为 O(n³),直接从邻接矩阵计算可达矩阵。
可达矩阵与连通性的关系:
- 无向图是连通的,当且仅当 P 的所有元素都是 1。
- 有向图是强连通的,当且仅当 P 的所有元素都是 1。
关联矩阵——边和顶点的关系
关联矩阵(incidence matrix)从另一个角度表示图:不是看"谁和谁相邻",而是看"每条边关联哪些顶点"。
给定图 G = (V, E),|V| = n,|E| = m,关联矩阵 B 是一个 n × m 的矩阵(行对应顶点,列对应边),定义如下:
对于无向图:
对于有向图:
直觉类比:邻接矩阵像"人与人之间的关系表"(谁认识谁),关联矩阵像"每条路连接哪两个路口的登记表"。
关联矩阵的一个性质:在无向图中,关联矩阵每列恰好有两个 1(因为每条边有两个端点)。在有向图中,每列恰好有一个 +1 和一个 -1。
三种矩阵的对比
| 矩阵 | 大小 | 行列含义 | 回答的问题 |
|---|---|---|---|
| 邻接矩阵 A | n × n | 行和列都是顶点 | 谁和谁相邻? |
| 可达矩阵 P | n × n | 行和列都是顶点 | 谁能到达谁? |
| 关联矩阵 B | n × m | 行是顶点,列是边 | 每条边连接哪些顶点? |
5.4 最短路径——Dijkstra 算法
问题描述
带权图(weighted graph):每条边都有一个数值,称为权重(weight)或权值。权重可以代表距离、时间、费用等。
最短路径问题:给定一个带权图和一个源点(起始顶点),求从源点到图中其他每个顶点的最短路径(权重之和最小的路径)。
直觉类比:你在地图上查路线,每条路有距离。你想知道从家到各个目的地的最短距离分别是多少。
Dijkstra 算法的核心思想
Dijkstra 算法(由荷兰计算机科学家 Edsger W. Dijkstra 于 1956 年提出)是一个贪心算法。
贪心策略:从源点出发,每次选择"当前已知距离最近的、还没被处理过的"顶点,用它去更新邻居的距离。为什么这样做是对的?因为所有边权都是非负的,所以一旦我们确定了某个顶点的最短距离,就不可能通过其他路径找到更短的距离了。
直觉类比:想象你在山区里,想找到从营地到各个村庄的最短山路。你的策略是:先把最近的村庄确定下来(因为山路都是上坡或平路,不可能绕远路反而更近),然后从这个村庄出发探索它的邻居,更新距离,再确定下一个最近的村庄……
前提条件:所有边的权重必须非负(≥ 0)。如果有负权边,Dijkstra 算法可能给出错误结果。
算法步骤(详细版)
设源点为 s,用 dist[v] 表示"当前已知的从 s 到 v 的最短距离",用 visited[v] 表示"顶点 v 是否已被最终确定"。
1 | 步骤 1(初始化): |
什么是"松弛"(relaxation)? 这是算法中的核心操作。想象一根橡皮筋从 s 拉到 v(当前最短路径 dist[v]),如果经过 u 再到 v 的距离更短,就像把橡皮筋松开重新拉紧,缩短了距离——所以叫"松弛"。
完整计算示例
考虑下面这个带权无向图,求从顶点 A 到其他各顶点的最短路径:
1 | 2 |
边和权重:
| 边 | 权重 |
|---|---|
| A-B | 2 |
| A-C | 1 |
| A-D | 3 |
| B-D | 1 |
| C-D | 5 |
| B-C | 4 |
(注意:这里 A-D 直接相连,B-C 也直接相连。)
初始状态:
| 顶点 | dist | visited | 说明 |
|---|---|---|---|
| A | 0 | false | 源点,距离为 0 |
| B | ∞ | false | 还不知道怎么到 B |
| C | ∞ | false | 还不知道怎么到 C |
| D | ∞ | false | 还不知道怎么到 D |
第 1 轮:选择 dist 最小的未访问顶点 → A(dist = 0)。
将 A 标记为已访问。用 A 的距离更新 A 的邻居:
- 邻居 B:dist[A] + w(A,B) = 0 + 2 = 2 < ∞ → 更新 dist[B] = 2
- 邻居 C:dist[A] + w(A,C) = 0 + 1 = 1 < ∞ → 更新 dist[C] = 1
- 邻居 D:dist[A] + w(A,D) = 0 + 3 = 3 < ∞ → 更新 dist[D] = 3
| 顶点 | dist | visited | 说明 |
|---|---|---|---|
| A | 0 | true | 已确定 |
| B | 2 | false | 经过 A:0+2=2 |
| C | 1 | false | 经过 A:0+1=1 |
| D | 3 | false | 经过 A:0+3=3 |
第 2 轮:选择 dist 最小的未访问顶点 → C(dist = 1)。
将 C 标记为已访问。用 C 的距离更新 C 的未访问邻居:
- 邻居 B:dist[C] + w(C,B) = 1 + 4 = 5,当前 dist[B] = 2,5 > 2 → 不更新(经过 C 到 B 更远)
- 邻居 D:dist[C] + w(C,D) = 1 + 5 = 6,当前 dist[D] = 3,6 > 3 → 不更新(经过 C 到 D 更远)
| 顶点 | dist | visited | 说明 |
|---|---|---|---|
| A | 0 | true | 已确定 |
| B | 2 | false | 仍然 2(经过 C 到 B 更远) |
| C | 1 | true | 已确定 |
| D | 3 | false | 仍然 3(经过 C 到 D 更远) |
第 3 轮:选择 dist 最小的未访问顶点 → B(dist = 2)。
将 B 标记为已访问。用 B 的距离更新 B 的未访问邻居:
- 邻居 D:dist[B] + w(B,D) = 2 + 1 = 3,当前 dist[D] = 3,3 = 3 → 不需要更新(一样短)
| 顶点 | dist | visited | 说明 |
|---|---|---|---|
| A | 0 | true | 已确定 |
| B | 2 | true | 已确定 |
| C | 1 | true | 已确定 |
| D | 3 | false | 仍然 3(经过 B 到 D 也是 3) |
第 4 轮:选择 dist 最小的未访问顶点 → D(dist = 3)。D 没有未访问邻居,标记为已访问。
| 顶点 | dist | visited | 说明 |
|---|---|---|---|
| A | 0 | true | 已确定 |
| B | 2 | true | 已确定 |
| C | 1 | true | 已确定 |
| D | 3 | true | 已确定 |
最终结果:
| 目的地 | 最短距离 | 最短路径 |
|---|---|---|
| A → B | 2 | A → B |
| A → C | 1 | A → C |
| A → D | 3 | A → D(直接走,或 A → B → D 都是 3) |
算法复杂度
- 朴素实现(每次线性扫描找最小 dist):O(|V|²) 适合稠密图(边很多的图)。
- 优先队列实现(用最小堆找最小 dist):O((|V| + |E|) log |V|) 适合稀疏图(边较少的图)。
5.5 关键路径——项目管理中的图论
什么是 AOE 网络?
在项目管理中,我们需要分析一个工程由哪些活动(任务)组成,以及这些活动之间的先后依赖关系。AOE 网络(Activity On Edge network,活动在边上)就是描述这种关系的一种图模型。
AOE 网络的约定:
- 顶点(也叫事件):表示某个阶段的完成状态。比如"需求分析完成"、"编码完成"等。
- 有向边(也叫活动):表示一个具体任务。边的起点是任务的"前提条件"(前置事件),终点是任务的"完成标志"(后续事件)。
- 边上的权重:表示该活动需要的持续时间。
AOE 网络有一个唯一的起点(入度为 0 的顶点,表示工程开始)和一个唯一的终点(出度为 0 的顶点,表示工程结束)。
直觉类比:AOE 网络就像一张"任务依赖图"。每个节点代表一个里程碑(比如"设计完成"),每条边代表一个任务(比如"编写代码"),边上的数字是这个任务需要多少天。
AOV 与 AOE 的区别
AOV 网络(Activity On Vertex):活动在顶点上,边表示先后顺序(不带权重)。适合表示"谁先谁后"的关系。
AOE 网络(Activity On Edge):活动在边上,顶点表示事件,边带权重表示活动耗时。适合分析"需要多久完成"和"哪些任务不能延迟"。
关键路径法——求最短工期和关键活动
关键路径(Critical Path):从起点到终点的最长路径。这条路径的长度就是整个工程的最短完成时间。
为什么是最长路径?因为工程要等所有活动都完成才能结束,所以完成时间取决于最慢的那条"链"。
关键活动:关键路径上的活动。这些活动没有余量(不能延迟一天,否则整个工程都会延迟)。
两个关键时间参数
为了找到关键路径,我们需要计算两个时间参数:
ve(j)——事件 j 的最早发生时间(earliest occurrence time):
表示事件 j 最早可以在什么时刻发生。计算方法:从起点出发,沿着所有可能的路径到达 j,取最长的那条(因为事件 j 要等所有前置活动都完成才能发生)。
计算公式(正向递推,从起点开始):
- 起点的 ve = 0
- 对于其他顶点 j:ve(j) = max{ve(i) + w(i, j)},其中 i 遍历所有指向 j 的顶点
直觉类比:你上大学需要先读小学、再读初中、再读高中。你最早什么时候能上大学?取决于"小学→初中→高中→大学"这条最长的路(总时间最长的路径)。
vl(j)——事件 j 的最迟发生时间(latest occurrence time):
表示事件 j 最晚必须在什么时刻发生(如果超过这个时间,整个工程就会延迟)。
计算公式(逆向递推,从终点开始):
- 终点的 vl = ve(终点)(整个工程的最早完成时间)
- 对于其他顶点 i:vl(i) = min{vl(j) - w(i, j)},其中 j 遍历所有从 i 指向的顶点
直觉类比:你的毕业典礼定在6月30日。你最迟什么时候必须完成论文?取决于"从现在到毕业典礼之间最紧迫的那条路"。
关键活动的判定
对于每条边(活动)(i, j),计算:
- 最早开始时间 e(i,j) = ve(i)(活动最早可以在前置事件发生后立即开始)
- 最迟开始时间 l(i,j) = vl(j) - w(i,j)(活动最迟必须在这个时间开始,否则后续事件会延迟)
如果 e(i,j) = l(i,j),即最早开始时间等于最迟开始时间,那么这个活动就是关键活动——它没有任何余量,一天都不能延迟。
总余量(也叫松弛时间)= l(i,j) - e(i,j),表示这个活动可以延迟多少时间而不影响工程总工期。
完整计算示例
考虑下面的 AOE 网络(6 个事件,8 个活动):
1 | a₁=3 |
边(活动)和权重:
| 活动 | 起点 → 终点 | 权重(持续时间) |
|---|---|---|
| a₁ | ① → ② | 3 |
| a₂ | ① → ③ | 2 |
| a₃ | ③ → ④ | 4 |
| a₄ | ② → ④ | 2 |
| a₅ | ④ → ⑤ | 3 |
| a₆ | ② → ⑤ | 4 |
| a₇ | ④ → ⑥ | 2 |
| a₈ | ⑤ → ⑥ | 1 |
第一步:计算 ve(正向递推,从起点 ① 开始)
- ve(①) = 0(起点)
- ve(②) = ve(①) + w(①,②) = 0 + 3 = 3(只有 ① 指向 ②)
- ve(③) = ve(①) + w(①,③) = 0 + 2 = 2(只有 ① 指向 ③)
- ve(④) = max{ve(②) + w(②,④), ve(③) + w(③,④)} = max{3+2, 2+4} = max{5, 6} = 6
- 说明:④ 要等 ② 和 ③ 的活动都完成才能发生,取较长的那个。
- ve(⑤) = max{ve(④) + w(④,⑤), ve(②) + w(②,⑤)} = max{6+3, 3+4} = max{9, 7} = 9
- ve(⑥) = max{ve(④) + w(④,⑥), ve(⑤) + w(⑤,⑥)} = max{6+2, 9+1} = max{8, 10} = 10
| 事件 | ve |
|---|---|
| ① | 0 |
| ② | 3 |
| ③ | 2 |
| ④ | 6 |
| ⑤ | 9 |
| ⑥ | 10 |
工程的最早完成时间 = ve(⑥) = 10 天。
第二步:计算 vl(逆向递推,从终点 ⑥ 开始)
- vl(⑥) = ve(⑥) = 10(终点的最迟时间等于最早时间)
- vl(⑤) = vl(⑥) - w(⑤,⑥) = 10 - 1 = 9
- vl(④) = min{vl(⑤) - w(④,⑤), vl(⑥) - w(④,⑥)} = min{9-3, 10-2} = min{6, 8} = 6
- vl(③) = vl(④) - w(③,④) = 6 - 4 = 2
- vl(②) = min{vl(④) - w(②,④), vl(⑤) - w(②,⑤)} = min{6-2, 9-4} = min{4, 5} = 4
- vl(①) = min{vl(②) - w(①,②), vl(③) - w(①,③)} = min{4-3, 2-2} = min{1, 0} = 0
| 事件 | ve | vl |
|---|---|---|
| ① | 0 | 0 |
| ② | 3 | 4 |
| ③ | 2 | 2 |
| ④ | 6 | 6 |
| ⑤ | 9 | 9 |
| ⑥ | 10 | 10 |
第三步:计算每个活动的 e 和 l,确定关键活动
| 活动 | 起→终 | w | e = ve(起点) | l = vl(终点) - w | 余量 l-e | 关键? |
|---|---|---|---|---|---|---|
| a₁ | ①→② | 3 | ve(①) = 0 | vl(②) - 3 = 4-3 = 1 | 1 | 否 |
| a₂ | ①→③ | 2 | ve(①) = 0 | vl(③) - 2 = 2-2 = 0 | 0 | 是 |
| a₃ | ③→④ | 4 | ve(③) = 2 | vl(④) - 4 = 6-4 = 2 | 0 | 是 |
| a₄ | ②→④ | 2 | ve(②) = 3 | vl(④) - 2 = 6-2 = 4 | 1 | 否 |
| a₅ | ④→⑤ | 3 | ve(④) = 6 | vl(⑤) - 3 = 9-3 = 6 | 0 | 是 |
| a₆ | ②→⑤ | 4 | ve(②) = 3 | vl(⑤) - 4 = 9-4 = 5 | 2 | 否 |
| a₇ | ④→⑥ | 2 | ve(④) = 6 | vl(⑥) - 2 = 10-2 = 8 | 2 | 否 |
| a₈ | ⑤→⑥ | 1 | ve(⑤) = 9 | vl(⑥) - 1 = 10-1 = 9 | 0 | 是 |
关键活动:a₂, a₃, a₅, a₈
关键路径:① →③→ ④→⑤→⑥,长度 = 2 + 4 + 3 + 1 = 10 天
解读:
- 整个工程最快 10 天完成。
- 活动 a₂, a₃, a₅, a₈ 一天都不能延迟,否则整个工程会延期。
- 活动 a₁ 有 1 天余量,a₄ 有 1 天余量,a₆ 有 2 天余量,a₇ 有 2 天余量——它们可以适当延迟而不影响总工期。
5.6 着色——最少需要几种颜色?
为什么需要"着色"?
考虑以下实际问题:
- 地图着色:给地图上的每个国家涂色,要求相邻国家颜色不同。最少需要几种颜色?
- 考试排期:大学里有多门考试,如果一个学生同时选了两门课,这两门考试不能安排在同一时间段。最少需要多少个时间段?
- 频率分配:蜂窝网络中相邻基站不能使用相同频率。最少需要多少种频率?
这些问题都可以抽象为同一个数学问题:图着色。
顶点着色
顶点着色(vertex coloring):给图的每个顶点分配一种颜色,使得任意两个邻接的顶点颜色不同(相邻顶点不能同色)。
k-着色:使用不超过 k 种颜色的着色方案。
色数(chromatic number),记作 χ(G):能够完成图 G 的顶点着色所需的最少颜色数。即 χ(G) = 最小的 k 使得 G 是 k-可着色的。
直觉类比:色数就是"解决问题所需的最少资源种类"。比如考试排期中,色数就是最少需要多少个时间段。
色数的计算
确定一个图的色数是一个 NP-完全问题(对于一般图来说很难高效求解),但对于某些特殊图,色数有已知结论:
结论 1:完全图 的色数 χ() = n。
为什么? 中每对顶点都相邻,所以所有顶点都必须颜色不同,需要 n 种颜色。
结论 2:二部图的色数 χ = 2(前提是图至少有一条边)。
为什么? 二部图的顶点可以分成两组 A 和 B,组内没有边。给 A 中所有顶点涂颜色 1,B 中所有顶点涂颜色 2,就完成了着色。
结论 3:长度为偶数的环()的色数 χ = 2;长度为奇数的环()的色数 χ = 3。
为什么? 偶数环可以交替着色(红-蓝-红-蓝-…),首尾颜色不同。奇数环交替着色时首尾会同色,需要第三种颜色。
结论 4:四色定理(Four Color Theorem):任何平面图(可以画在平面上且边不交叉的图)的色数 χ ≤ 4。
这是图论中最著名的定理之一。它在 1852 年被提出为猜想,直到 1976 年才由 Appel 和 Haken 借助计算机证明——这是数学史上第一个主要依靠计算机辅助证明的定理,当时引起了巨大争议。
直觉类比:四色定理说的就是:任何地图最多只需要 4 种颜色就能让相邻区域不同色。你可以试试:随便画一张地图,看能不能用 4 种颜色填满。
色数的上界——一个有用的不等式
定理:对于任何图 G,χ(G) ≤ Δ(G) + 1,其中 Δ(G) 是图中顶点的最大度。
为什么? 用贪心着色法:按任意顺序给顶点着色,每个顶点最多有 Δ 个邻居,所以最多有 Δ 种颜色被邻居占用了,还有第 Δ+1 种颜色可用。
边着色
边着色(edge coloring):给每条边分配一种颜色,使得相邻的边(共享一个端点的边)颜色不同。
边色数 χ’(G):完成边着色所需的最少颜色数。
Vizing 定理:对于简单图,Δ(G) ≤ χ’(G) ≤ Δ(G) + 1。即边色数要么等于最大度,要么比最大度大 1。
例题与详解
例题 1:握手定理的应用
题目:一个图有 10 个顶点,其中 6 个顶点的度为 3,4 个顶点的度为 5。求这个图有多少条边。
分析:直接应用握手定理即可。
详解:
第一步:计算所有顶点的度之和。
第二步:由握手定理 ,得:
验证:奇度顶点的个数 = 6(度为3的)+ 4(度为5的)= 10,是偶数。符合握手定理的推论。
答案:这个图有 19 条边。
例题 2:矩阵表示与通路计数
题目:给定有向图 G 的邻接矩阵如下,求从 到 长度为 2 的通路数,以及从 到 长度为 3 的通路数。
分析:长度为 k 的通路数 = 的 (1,3) 元素。
详解:
第一步:计算 。
逐元素计算(以 (1,3) 位置为例):
完整计算:
从 到 长度为 2 的通路数 = = 1 条。
验证: 是唯一长度为 2 的通路。
第二步:计算 。
从 到 长度为 3 的通路数 = = 0 条。
验证:枚举所有可能的 :
- (终点不是 )
- (终点不是 )
- :需要边 (v₃,v₃),不存在
确实 0 条。
例题 3:Dijkstra 算法的详细执行
题目:在下面的带权有向图中,求从顶点 S 到其他各顶点的最短路径。
1 | 10 |
边和权重:S→A(10), S→B(5), B→C(3), A→C(1), C→D(7), A→C(2)(注意 A→C 有两条路径,取权重为 2 的那条,即 A 到 C 直接相连权重为 2)
修正图的描述(避免歧义):
| 边 | 权重 |
|---|---|
| S → A | 10 |
| S → B | 5 |
| B → C | 3 |
| A → C | 2 |
| C → D | 7 |
详解:
初始状态:
| 顶点 | dist | visited |
|---|---|---|
| S | 0 | false |
| A | ∞ | false |
| B | ∞ | false |
| C | ∞ | false |
| D | ∞ | false |
第 1 轮:选 S(dist = 0),标记为已访问。
更新 S 的邻居(只看有从 S 出发的边的顶点):
- A:dist[S] + w(S,A) = 0 + 10 = 10 < ∞ → dist[A] = 10
- B:dist[S] + w(S,B) = 0 + 5 = 5 < ∞ → dist[B] = 5
| 顶点 | dist | visited |
|---|---|---|
| S | 0 | true |
| A | 10 | false |
| B | 5 | false |
| C | ∞ | false |
| D | ∞ | false |
第 2 轮:未访问顶点中 dist 最小的是 B(dist = 5),标记为已访问。
更新 B 的邻居:
- C:dist[B] + w(B,C) = 5 + 3 = 8 < ∞ → dist[C] = 8
| 顶点 | dist | visited |
|---|---|---|
| S | 0 | true |
| A | 10 | false |
| B | 5 | true |
| C | 8 | false |
| D | ∞ | false |
第 3 轮:未访问顶点中 dist 最小的是 C(dist = 8),标记为已访问。
更新 C 的邻居:
- D:dist[C] + w(C,D) = 8 + 7 = 15 < ∞ → dist[D] = 15
| 顶点 | dist | visited |
|---|---|---|
| S | 0 | true |
| A | 10 | false |
| B | 5 | true |
| C | 8 | true |
| D | 15 | false |
第 4 轮:未访问顶点中 dist 最小的是 A(dist = 10),标记为已访问。
更新 A 的邻居:
- C:dist[A] + w(A,C) = 10 + 2 = 12,当前 dist[C] = 8,12 > 8 → 不更新
| 顶点 | dist | visited |
|---|---|---|
| S | 0 | true |
| A | 10 | true |
| B | 5 | true |
| C | 8 | true |
| D | 15 | false |
第 5 轮:D 是唯一未访问的顶点(dist = 15),标记为已访问。D 没有出边,无需更新。
最终结果:
| 目的地 | 最短距离 | 最短路径 |
|---|---|---|
| S → A | 10 | S → A |
| S → B | 5 | S → B |
| S → C | 8 | S → B → C |
| S → D | 15 | S → B → C → D |
例题 4:关键路径分析
题目:某软件项目有以下任务和依赖关系,用 AOE 网络表示。求关键路径和最短工期。
| 活动 | 前置事件 | 后续事件 | 耗时(天) |
|---|---|---|---|
| 需求分析 | ① | ② | 3 |
| 技术选型 | ① | ③ | 2 |
| 系统设计 | ② | ④ | 4 |
| 原型开发 | ③ | ④ | 5 |
| 编码实现 | ④ | ⑤ | 6 |
| 测试 | ⑤ | ⑥ | 3 |
详解:
第一步:画出 AOE 网络
1 | 需求分析=3 |
注意:② 和 ③ 都指向 ④,所以 ④ 要等 ② 和 ③ 的活动都完成。
第二步:计算 ve(正向递推)
- ve(①) = 0
- ve(②) = ve(①) + 3 = 3
- ve(③) = ve(①) + 2 = 2
- ve(④) = max{ve(②) + 4, ve(③) + 5} = max{3+4, 2+5} = max{7, 7} = 7
- ve(⑤) = ve(④) + 6 = 7 + 6 = 13
- ve(⑥) = ve(⑤) + 3 = 13 + 3 = 16
最短工期 = ve(⑥) = 16 天
第三步:计算 vl(逆向递推)
- vl(⑥) = 16
- vl(⑤) = vl(⑥) - 3 = 13
- vl(④) = vl(⑤) - 6 = 7
- vl(③) = vl(④) - 5 = 2
- vl(②) = vl(④) - 4 = 3
- vl(①) = min{vl(②) - 3, vl(③) - 2} = min{3-3, 2-2} = min{0, 0} = 0
第四步:确定关键活动
| 活动 | 起→终 | w | e = ve(起点) | l = vl(终点)-w | 余量 | 关键? |
|---|---|---|---|---|---|---|
| 需求分析 | ①→② | 3 | 0 | 3-3=0 | 0 | 是 |
| 技术选型 | ①→③ | 2 | 0 | 2-2=0 | 0 | 是 |
| 系统设计 | ②→④ | 4 | 3 | 7-4=3 | 0 | 是 |
| 原型开发 | ③→④ | 5 | 2 | 7-5=2 | 0 | 是 |
| 编码实现 | ④→⑤ | 6 | 7 | 13-6=7 | 0 | 是 |
| 测试 | ⑤→⑥ | 3 | 13 | 16-3=13 | 0 | 是 |
所有活动都是关键活动!这是因为两条路(①→②→④→⑤→⑥ 和 ①→③→④→⑤→⑥)长度相等(都是 16),所以每条路上的活动都不能延迟。
关键路径:①→②→④→⑤→⑥ 和 ①→③→④→⑤→⑥(两条并行的关键路径)
例题 5:图着色
题目:一个学校的期末考试有 7 门课:A, B, C, D, E, F, G。以下课程对有学生同时选修:(A,B), (A,C), (A,D), (B,C), (B,E), (C,D), (C,F), (D,F), (D,G), (E,F), (F,G)。每个时间段只能安排一门考试,有学生同时选修的两门课不能安排在同一时间段。最少需要多少个时间段?
分析:把课程看作顶点,"有学生同时选修"的关系看作边,构成一个图。问题转化为求这个图的色数 χ(G)。
详解:
第一步:画出图的结构。
1 | B |
更清楚地列出每个顶点的邻居:
- A 的邻居:B, C, D
- B 的邻居:A, C, E
- C 的邻居:A, B, D, F(度 = 4)
- D 的邻居:A, C, F, G(度 = 4)
- E 的邻居:B, F
- F 的邻居:C, D, E, G(度 = 4)
- G 的邻居:D, F
第二步:最大度 Δ = 4(C, D, F 的度都是 4),所以 χ ≤ Δ + 1 = 5。
第三步:尝试用更少的颜色。使用贪心法,按顶点顺序 A, B, C, D, E, F, G 依次着色:
- A:颜色 1(第一个顶点)
- B:邻居 A 有颜色 1 → 用颜色 2
- C:邻居 A 有颜色 1,邻居 B 有颜色 2 → 用颜色 3
- D:邻居 A 有颜色 1,邻居 C 有颜色 3 → 用颜色 2(颜色 2 可用,因为 D 和 B 不相邻)
- E:邻居 B 有颜色 2 → 用颜色 1(颜色 1 可用,因为 E 和 A 不相邻)
- F:邻居 C 有颜色 3,邻居 D 有颜色 2,邻居 E 有颜色 1 → 用颜色 4
- G:邻居 D 有颜色 2,邻居 F 有颜色 4 → 用颜色 1(颜色 1 可用,因为 G 和 A、E 不相邻)
着色结果:
| 顶点 | 颜色 |
|---|---|
| A | 颜色 1(时间段 1) |
| B | 颜色 2(时间段 2) |
| C | 颜色 3(时间段 3) |
| D | 颜色 2(时间段 2) |
| E | 颜色 1(时间段 1) |
| F | 颜色 4(时间段 4) |
| G | 颜色 1(时间段 1) |
验证:检查每条边,两个端点颜色都不同。✓
第四步:能否用 3 种颜色?尝试发现不可能——因为存在一个三角形 B-C-(B 的邻居是 A,C,E,C 的邻居是 A,B,D,F),而且 A-B-C 构成三角形(A-B, B-C, A-C 都有边),三角形需要 3 种颜色。进一步分析可知 3 种颜色不够(因为 F 与 C, D, E 相邻,而 C, D, E 分别需要不同颜色)。
答案:最少需要 4 个时间段。
本章速查表
| 概念 | 一句话解释 | 公式/要点 |
|---|---|---|
| 图 G = (V, E) | 顶点(实体)+ 边(关系)的集合 | V 非空有限,E 有限 |
| 无向图 | 边没有方向,关系是双向的 | 边写成 {u, v} |
| 有向图 | 边有方向,关系是单向的 | 边写成 (u, v) |
| 邻接 | 两个顶点之间有边直接连接 | u 和 v 是邻居 |
| 关联 | 一条边连接了某两个顶点 | 边 e 与顶点 v 相关联 |
| 度 deg(v) | 与顶点 v 关联的边的数量 | 自环贡献 2 |
| 入度/出度 | 有向图中指向/离开顶点的边数 | deg⁻(v) / deg⁺(v) |
| 握手定理 | 所有顶点的度之和 = 2 × 边数 | ∑deg(v) = 2|E| |
| 奇度顶点推论 | 度为奇数的顶点个数必为偶数 | 来自握手定理 |
| 完全图 | 每对顶点之间都有边 | 边数 = n(n-1)/2 |
| 二部图 | 顶点可分成两组,每条边跨组连接 | 组内无边 |
| 子图 | 保留部分顶点和部分边 | V’ ⊆ V, E’ ⊆ E |
| 通路 | 顶点和边交替的序列 | 长度 = 边的数量 |
| 简单通路 | 所有顶点都不重复的通路 | 不走回头路 |
| 回路 | 起点 = 终点的通路 | 走了一圈回到原点 |
| 连通图 | 任意两个顶点之间都有通路 | 不会"断开" |
| 连通分量 | 极大连通子图 | 不连通图的各个"岛" |
| 强连通 | 有向图中任意两点互相可达 | 两个方向都能走 |
| 邻接矩阵 A | A[i][j]=1 表示 vᵢ 和 vⱼ 相邻 | n×n 矩阵 |
| A^k 的含义 | (i,j) 元素 = 从 vᵢ 到 vⱼ 长度为 k 的通路数 | 邻接矩阵的幂 |
| 可达矩阵 P | P[i][j]=1 表示从 vᵢ 可达 vⱼ | 布尔或 |
| 关联矩阵 B | B[i][j]=±1 表示 vᵢ 与边 eⱼ 的关联关系 | n×m 矩阵 |
| Dijkstra 算法 | 贪心求单源最短路径,要求非负权 | 每次选最近的未访问顶点 |
| 松弛操作 | 如果经过新顶点到邻居更短,就更新距离 | dist[u] + w(u,v) < dist[v] |
| AOE 网络 | 活动在边上,顶点是事件,权是持续时间 | 用于项目管理 |
| ve(j) | 事件 j 的最早发生时间 | 正向递推:取 max |
| vl(j) | 事件 j 的最迟发生时间 | 逆向递推:取 min |
| 关键活动 | 最早开始 = 最迟开始的活动 | 余量 = 0,不能延迟 |
| 关键路径 | 最长路径,决定工程最短工期 | 关键活动构成的路径 |
| 顶点着色 | 给顶点分配颜色,相邻顶点不同色 | 颜色 = 资源 |
| 色数 χ(G) | 完成着色所需的最少颜色数 | χ() = n |
| 四色定理 | 任何平面图的色数 ≤ 4 | 1976 年计算机辅助证明 |
| Δ(G) | 图中顶点的最大度 | χ(G) ≤ Δ(G) + 1 |
第6章 特殊图
本章定位:第5章我们学习了图的基本概念——什么是顶点(图中的点)、边(连接两个顶点的线)、度(一个顶点连接的边的数量)、路径(沿着边从一个顶点走到另一个顶点的序列)等。这一章我们将研究四类具有特殊性质的图,它们各自对应着经典的现实问题。
驱动问题
在深入学习之前,让我们先看看这四类特殊图各自要解决什么问题:
-
二部图:某公司有5个应聘者和4个岗位,每个应聘者能胜任某些岗位。如何安排才能让尽可能多的人上岗?这就是匹配问题——在二部图中寻找最优配对。
-
欧拉图:快递员需要走遍城市的每一条街道,每条街恰好走一次,最后回到快递站。这样的路线存在吗?这就是一笔画问题,源于1736年著名的哥尼斯堡七桥问题。
-
哈密顿图:旅行商需要访问5个城市各一次后回到出发地,如何规划最短路线?这就是旅行商问题(TSP),至今仍是计算机科学中最著名的难题之一。
-
平面图:设计一块电路板,上面有多个元件需要用导线连接,但导线之间不能交叉。这样的设计可能吗?这就是平面嵌入问题。
前置知识回顾
重要提示:如果你对图论的基本概念已经熟悉,可以跳过这一节。否则请先阅读以下内容。
图的基本概念回顾
图(Graph):由顶点(Vertex,也叫节点)和边(Edge)组成的数学结构。我们用 G = (V, E) 表示一个图,其中 V 是顶点的集合,E 是边的集合。
边:连接两个顶点的线。如果边有方向,称为有向边;如果没有方向,称为无向边。本章我们主要讨论无向图(所有边都没有方向)。
度(Degree):一个顶点的度是指与它相连的边的数量。顶点 v 的度记作 deg(v)。
直觉:度就像是一个人的"社交圈大小"——度越大,说明这个顶点与越多其他顶点相连。
路径(Path):从一个顶点出发,沿着边走到另一个顶点的顶点序列。路径中边不重复。
回路(Circuit):起点和终点相同的路径。回路中边不重复。
连通图(Connected Graph):任意两个顶点之间都存在路径的图。
简单图(Simple Graph):没有自环(一条边的两个端点是同一个顶点)也没有重边(两个顶点之间有多条边)的图。本章我们只讨论简单图。
6.1 二部图
为什么需要二部图?
想象一个招聘场景:有若干应聘者和若干岗位,每个应聘者能胜任某些岗位。我们希望找到一种配对方式,让尽可能多的应聘者上岗。这种"两边配对"的问题天然适合用二部图来建模。
概念讲解
二部图(Bipartite Graph):一个图 G = (V, E) 称为二部图,如果它的顶点集 V 可以分成两个不相交的子集 V₁ 和 V₂(即 V₁ ∩ V₂ = ∅,且 V₁ ∪ V₂ = V),使得图中的每一条边都连接 V₁ 中的一个顶点和 V₂ 中的一个顶点。换句话说,同一个子集内部的顶点之间没有边相连。
直觉:二部图就像两个阵营——阵营内部的成员互不认识,所有的联系都发生在两个阵营之间。比如:
- 学生和课程(学生选课关系):学生在一边,课程在另一边,选课关系连接两边
- 求职者和岗位(应聘关系):求职者在一边,岗位在另一边
- 男性和女性(婚姻关系):男性在一边,女性在另一边
判定定理
定理(二部图的判定):一个图是二部图 当且仅当 它不包含奇数长度的回路。
奇数长度的回路:回路中包含奇数条边。
直觉解释:为什么奇数长度的回路会"破坏"二部图的性质?
假设我们尝试把图的顶点分成两组(V₁ 和 V₂),用两种颜色标记(比如红色和蓝色)。二部图要求:每条边必须连接不同颜色的顶点。
- 从 V₁(红色)出发,经过1条边到达 V₂(蓝色)
- 再经过1条边回到 V₁(红色)
- 再经过1条边到达 V₂(蓝色)
你发现规律了吗?经过偶数条边后会回到原来的颜色,经过奇数条边后会到达另一种颜色。
如果一个回路有奇数条边,从某个颜色出发,走完回路后应该回到起点(原来的颜色),但实际上走了奇数条边后到达了另一种颜色——这就产生了矛盾!所以奇数长度的回路无法被二着色,因此不是二部图。
匹配
匹配(Matching):图的一个边的子集,其中任意两条边都没有公共顶点。也就是说,匹配中的边两两不相邻。
直觉:匹配就像是"配对"——每条边代表一对配对关系,匹配要求每个顶点最多只能参与一次配对。
最大匹配(Maximum Matching):包含边数最多的匹配。
完美匹配(Perfect Matching):匹配覆盖了图中的所有顶点。也就是说,每个顶点都恰好是匹配中某条边的端点。
饱和(Saturated):如果一个顶点是匹配中某条边的端点,则称该顶点被匹配饱和。
直觉:
- 匹配:一对一的配对关系,每个人最多配一次
- 最大匹配:配对数量最多
- 完美匹配:所有人都找到了配对(前提是总人数是偶数)
核心工具
霍尔定理(Hall’s Theorem):在二部图 G = (V₁ ∪ V₂, E) 中,存在一个匹配覆盖 V₁ 的所有顶点 当且仅当 对于 V₁ 的任意子集 S,S 的邻居集合 N(S) 满足 |N(S)| ≥ |S|。
直觉:霍尔定理说的是"如果 V₁ 中任意 k 个人共同能胜任的岗位数都不少于 k,那么就能让所有人都上岗"。这是匹配问题的核心判定条件。
6.2 欧拉图
为什么需要欧拉图?
1736年,数学家欧拉(Leonhard Euler)研究了一个著名问题:哥尼斯堡城有四个区域(用 A、B、C、D 表示)和七座桥连接它们。问题是:能否从某个区域出发,每座桥恰好走一次,最后回到出发点?
这个问题开创了图论这个数学分支。欧拉的解决方案简单而优美——他发现答案只取决于每个区域连接的桥的数量(即每个顶点的度)。
概念讲解
欧拉回路(Euler Circuit):一条经过图中每条边恰好一次,并且起点和终点相同的回路。
欧拉通路(Euler Path):一条经过图中每条边恰好一次的通路(起点和终点可以不同)。
欧拉图(Eulerian Graph):存在欧拉回路的图。
直觉:
- 欧拉回路:一笔画完所有边,最后回到起点
- 欧拉通路:一笔画完所有边,但最后不需要回到起点
核心工具:判定条件
定理(欧拉回路的存在条件):一个连通图存在欧拉回路 当且仅当 每个顶点的度都是偶数。
定理(欧拉通路的存在条件):一个连通图存在欧拉通路 当且仅当 它恰好有0个或2个奇度顶点。
- 如果有0个奇度顶点:存在欧拉回路(起点=终点)
- 如果有2个奇度顶点:存在欧拉通路(起点和终点就是这两个奇度顶点)
为什么要求偶数度?直觉解释:
想象你在图中沿着边行走。每当你进入一个顶点,你必须从这个顶点离开(否则就"卡住"了)。也就是说,每个顶点的"进入次数"和"离开次数"必须相等。
对于起点:你从这里出发(1次离开),然后每次进入后都要离开,最后回到这里(1次进入)。所以起点的总次数 = 离开1次 + (进入+离开)若干次 + 进入1次 = 偶数。
对于其他顶点:每次进入后都要离开,所以总次数 = (进入+离开)若干次 = 偶数。
因此,如果存在欧拉回路,每个顶点的度(即经过它的次数)都必须是偶数。
如果只有2个奇度顶点呢?这两个顶点一个是起点(多1次离开),一个是终点(多1次进入),其余顶点仍然是偶数度。
哥尼斯堡七桥问题的解答
让我们用判定条件来解决七桥问题:
1 | 哥尼斯堡七桥问题的图表示: |
奇度顶点有 A(度3)和 B(度3),共2个。
根据判定条件:存在欧拉通路(从 A 到 B,或从 B 到 A),但不存在欧拉回路。
所以答案是:不可能从某地出发走遍所有桥并回到出发地,但可以从北岸出发走到南岸(或反过来)。
约定与记号
- deg(v):顶点 v 的度
- 奇度顶点:度为奇数的顶点
- 偶度顶点:度为偶数的顶点
- 连通:任意两个顶点之间都存在路径
6.3 哈密顿图
为什么需要哈密顿图?
旅行商问题(TSP):一个商人需要访问 n 个城市,每个城市恰好访问一次,最后回到出发城市。他想知道:这样的路线是否存在?如果存在,最短的路线是什么?
这个问题的核心是寻找哈密顿回路——经过图中每个顶点恰好一次的回路。
概念讲解
哈密顿回路(Hamiltonian Circuit):一条经过图中每个顶点恰好一次,并且起点和终点相同的回路。
哈密顿通路(Hamiltonian Path):一条经过图中每个顶点恰好一次的通路。
哈密顿图(Hamiltonian Graph):存在哈密顿回路的图。
欧拉图 vs 哈密顿图——关键区别:
欧拉图 哈密顿图 关注什么 边 顶点 要求 每条边恰好经过一次 每个顶点恰好经过一次 判定条件 有简单的充要条件 没有简单的充要条件 直觉类比:
- 欧拉图:快递员要走遍每条街道(边),每条街只走一次
- 哈密顿图:旅行商要访问每个城市(顶点),每个城市只去一次
判定条件
不幸的是,哈密顿图没有像欧拉图那样简单而实用的充要条件。这是图论中著名的难题——判定一个图是否是哈密顿图是一个 NP 完全问题,目前没有已知的高效算法。
必要条件(只能否定,不能肯定)
定理:如果图 G 有哈密顿回路,那么对于任意非空顶点子集 S ⊆ V(G),删除 S 中的所有顶点后,剩余图的连通分量数不超过 |S|。
直觉:哈密顿回路像一条"蛇"一样穿过所有顶点。如果你删掉几个顶点(|S|个),这条"蛇"最多被切成 |S|+1 段,所以连通分量数不超过 |S|。
重要:这个条件只能用来否定——如果不满足,一定不是哈密顿图;如果满足,不能肯定就是哈密顿图。
充分条件(只能肯定,不能否定)
狄拉克定理(Dirac’s Theorem):对于 n ≥ 3 的简单图,如果每个顶点的度都至少为 n/2,则图有哈密顿回路。
奥尔定理(Ore’s Theorem):对于 n ≥ 3 的简单图,如果对于任意两个不相邻的顶点 u 和 v,都有 deg(u) + deg(v) ≥ n,则图有哈密顿回路。
直觉:
- 狄拉克定理:如果每个顶点都"很受欢迎"(度很大),那么一定能找到哈密顿回路
- 奥尔定理:狄拉克定理的推广——即使有些顶点度不大,只要任意两个不相邻的顶点的度之和足够大,也能找到哈密顿回路
重要:这些条件只是充分条件——满足就一定有哈密顿回路,但不满足也可能是哈密顿图。
核心工具
完全图的哈密顿回路
完全图(Complete Graph):任意两个顶点之间都有边相连的图,记作 Kₙ(n 个顶点的完全图)。
定理:对于 n ≥ 3,完全图 Kₙ 一定有哈密顿回路。
直觉:在完全图中,你可以任意安排顶点的访问顺序,因为任意两个顶点之间都有边相连。比如 K₅ 中,你可以按任意顺序访问5个顶点,最后回到起点。
旅行商问题(TSP)
旅行商问题(Traveling Salesman Problem):给定 n 个城市和它们之间的距离(或旅费),找到访问每个城市恰好一次并回到起点的最短路线。
- 存在性:如果城市之间的连接构成完全图(任意两个城市之间都有直达路线),则一定存在哈密顿回路
- 最优解:找到最短的哈密顿回路是 NP 难问题——目前已知没有高效算法(多项式时间算法)
- 近似方法:实际中常用贪心算法、最近邻算法、遗传算法等寻找近似最优解
约定与记号
- Kₙ:n 个顶点的完全图
- NP 难问题:一类计算上非常困难的问题,目前没有已知的高效解法
- 充分条件:满足条件则结论成立,但不满足时结论可能成立也可能不成立
- 必要条件:结论成立则条件必须满足,但满足条件时结论不一定成立
6.4 平面图
为什么需要平面图?
想象你要设计一块电路板,上面有多个电子元件(顶点),需要用导线(边)连接它们。但是,导线之间不能交叉,否则会造成短路。你需要判断:给定的连接方案能否在平面上实现而不交叉?
这就是平面图研究的问题。
概念讲解
平面图(Planar Graph):一个图称为平面图,如果它可以画在平面上使得边只在顶点处相交(即边不交叉)。
平面嵌入(Planar Embedding):将平面图画在平面上使得边不交叉的一种具体画法。
重要理解:判断一个图是否是平面图,不是看它当前的画法是否交叉,而是看是否存在一种画法不交叉。同一个图可能有很多种画法,有的画法有交叉,有的画法没有交叉。
面(Face):平面嵌入将平面分割成若干个区域,每个区域称为一个面。其中有一个外部面(也叫无限面),是平面中无界的区域。
直觉:想象一个画在纸上的图,纸上的每个"封闭区域"就是一个面。纸的"外面"(无穷远处)也算一个面。
核心工具:欧拉公式
欧拉公式:对于一个连通的平面图 G,设 V 是顶点数,E 是边数,F 是面数,则:
验证例子:
例1:三角形(3个顶点,3条边)
1
2
3
4 A
/ \
/ \
B-----C
- V = 3, E = 3
- 面数 F = 2(三角形内部1个面 + 外部1个面)
- 验证:3 - 3 + 2 = 2 ✓
例2:四边形(4个顶点,4条边)
1
2
3 A---B
| |
D---C
- V = 4, E = 4
- 面数 F = 2(四边形内部1个面 + 外部1个面)
- 验证:4 - 4 + 2 = 2 ✓
例3:四边形加一条对角线
1
2
3
4 A---B
|\ |
| \ |
D--\C
- V = 4, E = 5
- 面数 F = 3(左三角形1个面 + 右三角形1个面 + 外部1个面)
- 验证:4 - 5 + 3 = 2 ✓
欧拉公式的推论
推论1:对于简单连通平面图,边数 E ≤ 3V - 6。
推论2:对于不含三角形(即没有长度为3的回路)的简单连通平面图,边数 E ≤ 2V - 4。
直觉:这些推论给出了平面图"边数的上限"。如果你的图边数超过了这个上限,它一定不是平面图。
库拉托夫斯基定理
两个关键的非平面图
K₅:5个顶点的完全图(任意两个顶点之间都有边)。
1 | K₅ 的示意图(无法画成平面图): |
K₃,₃:完全二部图,两组各有3个顶点,每组的每个顶点都与另一组的所有顶点相连(共9条边)。
1 | K₃,₃ 的示意图(无法画成平面图): |
为什么 K₅ 和 K₃,₃ 不是平面图?
用推论1检验 K₅:V=5, E=10,但 3V-6 = 3×5-6 = 9 < 10,所以 K₅ 一定不是平面图。
用推论2检验 K₃,₃:K₃,₃ 是二部图,不含三角形。V=6, E=9,但 2V-4 = 2×6-4 = 8 < 9,所以 K₃,₃ 一定不是平面图。
细分
细分(Subdivision):在一条边上插入一个新的顶点,把这条边分成两条边。细分操作不改变图的平面性。
直觉:细分就像是把一条边"掰弯"成两段,中间加一个点。这样做不会改变图能否画成平面图的性质。
1 | 细分的例子: |
库拉托夫斯基定理
定理(Kuratowski’s Theorem):一个图是平面图 当且仅当 它不包含 K₅ 或 K₃,₃ 的细分作为子图。
直觉:K₅ 和 K₃,₃ 是"最小的非平面图"——它们本身不能画在平面上不交叉,但去掉任何一条边就可以。任何非平面图都"本质上"包含 K₅ 或 K₃,₃ 的结构。
判定方法总结
判断一个图是否是平面图,可以按以下步骤:
- 尝试画出平面嵌入:如果成功,就是平面图
- 用欧拉公式的推论检查边数上限:
- 如果 E > 3V - 6,一定不是平面图
- 如果图不含三角形且 E > 2V - 4,一定不是平面图
- 检查是否包含 K₅ 或 K₃,₃ 的细分:如果包含,不是平面图
约定与记号
- K₅:5个顶点的完全图
- K₃,₃:两组各3个顶点的完全二部图
- V, E, F:分别表示顶点数、边数、面数
- 平面嵌入:图在平面上的一种不交叉的画法
例题与详解
例题1:判断二部图
问题:判断以下图是否是二部图:
1 | 图 G:顶点 {A, B, C, D, E} |
整体分析:使用判定定理——检查图中是否有奇数长度的回路。
详解:
首先,让我们尝试给顶点二着色(分成两组):
- A 放入 V₁(第一组)
- B 与 A 相连,放入 V₂(第二组)
- C 与 B 相连,放入 V₁
- D 与 A 相连,放入 V₂
- E 与 D 相连,放入 V₁
检查所有边是否都连接不同组的顶点:
- {A,B}:A∈V₁, B∈V₂ ✓
- {A,D}:A∈V₁, D∈V₂ ✓
- {B,C}:B∈V₂, C∈V₁ ✓
- {C,D}:C∈V₁, D∈V₂ ✓
- {D,E}:D∈V₂, E∈V₁ ✓
- {B,E}:B∈V₂, E∈V₁ ✓
所有边都连接不同组的顶点,所以图 G 是二部图。
验证:检查是否有奇数长度的回路——经过检查,图中的回路都是偶数长度(如 A-B-C-D-A 长度为4),没有奇数长度的回路。
例题2:判断欧拉图
问题:判断以下图是否存在欧拉回路或欧拉通路:
1 | 图 G:顶点 {A, B, C, D, E} |
整体分析:计算每个顶点的度,用判定条件判断。
详解:
计算各顶点的度:
- deg(A) = 3(连接到 B, C, D)
- deg(B) = 2(连接到 A, C)
- deg© = 4(连接到 A, B, D, E)
- deg(D) = 3(连接到 A, C, E)
- deg(E) = 2(连接到 C, D)
统计奇度顶点:A(度3)和 D(度3),共2个奇度顶点。
图是连通的(任意两个顶点之间都有路径)。
根据判定条件:
- 恰好2个奇度顶点 → 存在欧拉通路(从 A 到 D,或从 D 到 A)
- 不存在欧拉回路(因为有奇度顶点)
例题3:判断哈密顿图
问题:判断以下图是否有哈密顿回路:
1 | 图 G:顶点 {A, B, C, D, E} |
整体分析:使用奥尔定理(充分条件)检验。
详解:
首先,n = 5(有5个顶点)。
列出所有不相邻的顶点对:
- A 和 E:deg(A) = 3, deg(E) = 3, deg(A) + deg(E) = 6 ≥ 5 ✓
检查所有不相邻的顶点对,发现只有 A 和 E 不相邻,且 deg(A) + deg(E) = 6 ≥ 5。
根据奥尔定理,图 G 有哈密顿回路。
寻找哈密顿回路:A → B → C → D → E → A(验证:A-B 有边,B-C 有边,C-D 有边,D-E 有边,E-A 有边?不对,A 和 E 不相邻!)
重新寻找:A → B → E → D → C → A(验证:A-B 有边,B-E 有边,E-D 有边,D-C 有边,C-A 有边 ✓)
所以哈密顿回路是 A → B → E → D → C → A。
例题4:欧拉公式验证
问题:一个连通平面图有 6 个顶点和 10 条边,求面数,并验证它是否可能是平面图。
整体分析:用欧拉公式求面数,用推论检验边数上限。
详解:
由欧拉公式 V - E + F = 2:
6 - 10 + F = 2
F = 6
所以这个图有6个面。
验证边数上限:E ≤ 3V - 6 = 3×6 - 6 = 12
因为 10 ≤ 12,所以边数满足上限条件。
结论:这个图可能是平面图(但还需要进一步验证是否真的能画成平面图)。边数上限只是必要条件,不是充分条件。
例题5:判断平面图
问题:判断 K₅ 是否是平面图。
整体分析:用欧拉公式的推论检验。
详解:
K₅ 有 V = 5 个顶点,E = 10 条边。
用推论1检验:E ≤ 3V - 6 = 3×5 - 6 = 9
但是 E = 10 > 9,所以 K₅ 不是平面图。
直观理解:你可以尝试画 K₅,无论如何画,总会有边交叉。
本章速查表
| 概念 | 定义/判定 | 一句话解释 |
|---|---|---|
| 二部图 | 顶点可分成两组,边只在两组之间 | 两个阵营,内部无联系,联系只在两阵营之间 |
| 二部图判定 | 不含奇数长度回路 | 能用两种颜色着色,相邻顶点颜色不同 |
| 匹配 | 边的子集,任意两条边无公共顶点 | 一对一配对,每人最多配一次 |
| 最大匹配 | 包含最多边的匹配 | 配对数量最多 |
| 完美匹配 | 覆盖所有顶点的匹配 | 所有人都找到配对 |
| 欧拉回路 | 经过每条边恰好一次的回路 | 一笔画完所有边,回到起点 |
| 欧拉通路 | 经过每条边恰好一次的通路 | 一笔画完所有边,不回到起点 |
| 欧拉回路条件 | 连通 + 所有顶点度为偶数 | 每次进入都能离开,度必须是偶数 |
| 欧拉通路条件 | 连通 + 恰好2个奇度顶点 | 起点和终点是两个奇度顶点 |
| 哈密顿回路 | 经过每个顶点恰好一次的回路 | 访问所有城市各一次,回到起点 |
| 哈密顿通路 | 经过每个顶点恰好一次的通路 | 访问所有城市各一次,不回到起点 |
| 狄拉克定理 | 每个顶点度 ≥ n/2 → 哈密顿 | 每个顶点都"很受欢迎",一定有哈密顿回路 |
| 奥尔定理 | 不相邻顶点度之和 ≥ n → 哈密顿 | 任意两个不相邻的顶点度之和足够大 |
| 平面图 | 可画在平面上边不交叉 | 存在一种画法,边只在顶点处相交 |
| 平面嵌入 | 平面图的一种不交叉画法 | 图在平面上的具体画法,边不交叉 |
| 面 | 平面嵌入将平面分割成的区域 | 纸上图的每个"封闭区域" |
| 欧拉公式 | V - E + F = 2(连通平面图) | 顶点数-边数+面数=2 |
| 平面图边数上限 | E ≤ 3V - 6 | 边数不能太多,否则一定不是平面图 |
| K₅ | 5个顶点的完全图 | 最小的非平面图之一 |
| K₃,₃ | 两组各3个顶点的完全二部图 | 最小的非平面图之一 |
| 细分 | 在边上插入新顶点,把边分成两段 | 不改变平面性的操作 |
| 库拉托夫斯基定理 | 不含 K₅ 或 K₃,₃ 细分 ⟺ 平面图 | 非平面图"本质上"包含 K₅ 或 K₃,₃ |
关键对比:欧拉图 vs 哈密顿图
| 欧拉图 | 哈密顿图 | |
|---|---|---|
| 关注对象 | 边 | 顶点 |
| 要求 | 每条边恰好经过一次 | 每个顶点恰好经过一次 |
| 判定条件 | 有简单充要条件(度的条件) | 没有简单充要条件 |
| 计算复杂度 | 有多项式时间算法 | NP 完全问题 |
| 现实类比 | 快递员走遍每条街道 | 旅行商访问每个城市 |
| 历史起源 | 1736年七桥问题 | 1859年哈密顿的"环游世界"游戏 |
第7章 树
本章定位:树是图的特例——无圈连通图,是计算机科学中最重要的结构之一。本章不假设你有图论的前置知识,必要概念会重新解释。
驱动问题
你的电脑里有几万个文件,怎么组织才能快速找到任何一个?
答案是文件夹→子文件夹→文件——这就是一棵树。你打开文件资源管理器,看到的目录结构就是一棵倒过来的树:最上面是"此电脑"(根),下面分出各个磁盘(子节点),再下面分出文件夹,最下面是文件(叶子)。
树无处不在:
- 网络布线:连接 N 个节点,要用最少的线(最少边 = n-1),同时保证任意两点可达——这就是生成树。
- 压缩算法:英文字母 e 出现频率最高,给它最短的编码;z 出现最少,给它最长的编码——霍夫曼编码就是一棵二叉树。
- 决策树:医生诊断时"先问有没有发烧→有就查核酸→没有就查血压",这就是一棵决策树。
7.1 无向树
为什么需要树?
在第5章和第6章中,我们学了图的各种性质。但有一类特殊的图特别重要——没有回路的连通图。这种图的结构像一棵树:从任意一点出发,到另一点只有唯一的路径。这种唯一性带来了很多优良性质。
概念讲解
什么是图? 回顾一下:图(Graph)由顶点(也叫节点)和边组成。边连接两个顶点,表示它们之间的关系。
1 | 例如:顶点 = {A, B, C},边 = {A-B, B-C} |
无向图:边没有方向。边 A-B 表示 A 和 B 之间的连接是双向的。
连通(Connected):图中任意两个顶点之间都有路径相连。路径就是从一个顶点出发,沿着边走到另一个顶点的路线。
回路(Cycle):从一个顶点出发,沿着边走,最终回到同一个顶点的路线,且中途不重复经过任何边。
1 | 有回路的图: 无回路的图: |
无向树(Undirected Tree):满足以下两个条件的图:
- 连通——任意两个顶点之间都有路径
- 无回路——图中不存在任何回路
直觉类比:树就像一棵真正的树——从树根到任何一片叶子只有唯一的一条路。如果有多条路,那就形成了"圈",就不是树了。
森林(Forest):无回路的图,但不一定连通。森林由多棵独立的树组成。
1 | 森林(两棵独立的树): |
树的等价定义
以下条件互相等价——满足任意一个,就一定是树:
| 条件 | 含义 |
|---|---|
| ① 连通且无回路 | 树的定义 |
| ② 任意两个顶点之间有唯一的简单路径 | “唯一性”——没有第二条路 |
| ③ 连通且边数 = 顶点数 - 1(|E| = |V| - 1) | “最少的边保证连通” |
| ④ 无回路且边数 = 顶点数 - 1(|E| = |V| - 1) | “最多个顶点不形成回路” |
为什么这些条件等价? 直觉:
- ②是①的推论:连通保证有路径,无回路保证路径唯一
- ③说明树用"刚好够"的边数实现连通——少一条就不连通,多一条就形成回路
- ④说明树是"刚好不形成回路"的最大边数
树的关键性质
性质1:n 个顶点的树恰好有 n-1 条边。
为什么?想象从一个顶点开始,每次加一个顶点和一条边(这样不形成回路)。n 个顶点需要加 n-1 次,所以有 n-1 条边。
性质2:非平凡树(至少2个顶点的树)至少有 2 个叶子。
叶子(Leaf):度为 1 的顶点——只连接一条边的顶点。度就是与一个顶点关联的边的数量。
为什么至少2个叶子?从任意顶点出发,沿简单路径走到底(因为无回路,不会绕回来),走到"走不下去"的顶点——那就是叶子。从不同起点走,会得到不同的叶子。
性质3:树中删去任意一条边,图就不再连通(变成两棵树)。
为什么?因为每条边都是"唯一路径"的一部分——删掉它,两端的顶点就无法互通了。
7.2 生成树
为什么需要生成树?
给定一个连通图,它可能有很多回路。如果我们想用最少的边保持所有顶点的连通性,就需要找到它的一个"骨架"——这就是生成树。
概念讲解
生成树(Spanning Tree):图 G 的一个子图,它满足:
- 包含 G 的所有顶点
- 本身是一棵树(连通且无回路)
直觉:生成树是原图的"骨架"——保留所有顶点,去掉多余的边(只去掉形成回路的边),直到没有回路。
1 | 原图(有回路): 生成树(无回路): |
构造方法:DFS 和 BFS
深度优先搜索(DFS,Depth-First Search):从一个起始顶点出发,尽可能深地探索——能往下走就往下走,走不通了再回退到上一个岔路口,换一条路继续走。
类比:走迷宫时"一直往左走,撞墙了就退回来换右边"。
广度优先搜索(BFS,Breadth-First Search):从一个起始顶点出发,先访问所有直接邻居,再访问邻居的邻居,一层一层往外扩展。
类比:往池塘里扔石头——波纹从中心一圈一圈向外扩散。
DFS 和 BFS 都能构造生成树——走过的边构成一棵树。
最小生成树
最小生成树(MST,Minimum Spanning Tree):带权图中,总权值最小的生成树。
什么是带权图?边上有数字(权值)表示代价、距离、费用等。例如城市之间的公路距离。
Kruskal 算法(全局贪心):
核心思想:先把所有边按权值从小到大排序,然后依次考虑每条边——如果加入这条边不会形成回路,就保留它;如果会形成回路,就跳过。直到选了 n-1 条边为止。
直觉:像修路——先修最便宜的路,能连就连,不能连(会形成环)就跳过。
Prim 算法(局部贪心):
核心思想:从任意一个顶点开始,每次选择连接"已选顶点集"和"未选顶点集"的最小权边,把对应的未选顶点加入已选集。重复直到所有顶点都被选中。
直觉:像滚雪球——从一个点开始,每次都选最便宜的边把新节点"拉进来"。
7.3 根树
为什么需要根树?
前面的"树"是无向的,没有方向。但很多实际场景需要有方向——文件系统中,文件夹"包含"子文件夹,不是反过来。家族树中,父母"生"子女,不是反过来。这就需要根树。
概念讲解
根树(Rooted Tree):指定一个顶点为根(Root)的有向树,所有边的方向都从根指向叶子。
直觉:像一棵真正的树——树根在下面,树枝向上生长。但在计算机科学中,习惯把根画在上面,向下生长(倒过来的树)。
1 | 根树示例: |
层次相关术语:
| 术语 | 含义 | 例子(上图) |
|---|---|---|
| 根(Root) | 没有父节点的顶点 | A |
| 叶子(Leaf) | 没有子节点的顶点 | D, E, F |
| 深度(Depth) | 从根到该顶点的距离(边数) | B 的深度 = 1, D 的深度 = 2 |
| 高度(Height) | 所有顶点的最大深度 | 树的高度 = 2 |
| 父节点 | 指向该顶点的顶点 | B 的父节点是 A |
| 子节点 | 该顶点指向的顶点 | A 的子节点是 B, C |
| 兄弟节点 | 有同一个父节点的顶点 | B 和 C 是兄弟 |
二叉树
二叉树(Binary Tree):每个顶点最多有 2 个子节点(左子和右子)的根树。
1 | 二叉树: |
注意:左子和右子是不同的——交换左右子树得到的是不同的二叉树。
满二叉树(Full Binary Tree):每个非叶节点恰好有 2 个子节点,且所有叶子在同一层。
1 | 满二叉树(高度2): |
完全二叉树(Complete Binary Tree):除最后一层外,每层都是满的;最后一层的节点从左到右连续排列。
1 | 完全二叉树: |
前缀码
编码问题:假设我们用二进制串(0 和 1 的序列)来表示字符。比如 A=0, B=10, C=110。
前缀码(Prefix Code):没有任何一个字符的编码是另一个字符编码的前缀(开头部分)。
为什么需要前缀码?如果没有这个约束,解码会产生歧义。
例如:A=0, B=01。收到 “01”,是 “A+?” 还是 “B”?无法判断!
有了前缀码约束:A=0, B=10, C=110。收到 “010”——第一个字符一定是 A(因为 0 是 A 的编码,且没有其他编码以 0 开头),剩下 “10” 是 B。无歧义!
霍夫曼编码
霍夫曼编码(Huffman Coding):用二叉树构造最优前缀码——频率高的字符用短编码,频率低的用长编码,使得总编码长度最短。
构造方法:
- 为每个字符创建一个叶子节点,权值为该字符的出现频率
- 每次选择权值最小的两个节点,合并为一个新节点(新节点的权值 = 两个子节点的权值之和)
- 新节点放在原来的节点池中,重复步骤 2
- 直到只剩一个根节点
- 从根到每个叶子的路径就是该字符的编码:左分支 = 0,右分支 = 1
直觉:频率高的字符靠近根(路径短 = 编码短),频率低的远离根(路径长 = 编码长)。这保证了总编码长度最短。
例题与详解
例题 1:树的性质证明
证明:n 个顶点的树恰好有 n-1 条边。
整体分析
用数学归纳法证明。归纳法的思路:先证明 n=1 时成立(基础),再证明"如果 n=k 时成立,则 n=k+1 时也成立"(归纳步骤)。
详解
基础:n=1 时,1 个顶点,0 条边。1-1=0 ✓。
归纳假设:假设 n=k 时成立,即 k 个顶点的树有 k-1 条边。
归纳步骤:考虑 n=k+1 个顶点的树 T。
- T 是树,至少有 2 个叶子(性质2)
- 删去一个叶子 v 及其关联的那条边 e
- 剩下的图 T’ 仍然是树——为什么?因为删叶子不破坏连通性(叶子只连一条边),也不引入回路(删边不会形成回路)
- T’ 有 k 个顶点,由归纳假设,有 k-1 条边
- T 比 T’ 多 1 个顶点和 1 条边
- 所以 T 有 (k-1) + 1 = k 条边 = (k+1) - 1
由数学归纳法,对所有 n ≥ 1 成立。 ∎
例题 2:Kruskal 算法
用 Kruskal 算法求以下图的最小生成树:
1 | 顶点:{A, B, C, D, E} |
整体分析
Kruskal 算法的步骤:排序 → 依次选最小边 → 不成回路就保留 → 选够 n-1 条边停止。
详解
第一步:按权值排序所有边
| 排序 | 边 | 权值 |
|---|---|---|
| 1 | (B,C) | 1 |
| 2 | (A,B) | 2 |
| 3 | (A,C) | 3 |
| 4 | (D,E) | 3 |
| 5 | (B,D) | 4 |
| 6 | (C,D) | 5 |
| 7 | (C,E) | 6 |
第二步:依次选边
5 个顶点的树需要 4 条边(n-1 = 5-1 = 4)。
- (B,C)=1:加入。目前选了 {(B,C)}。无回路 ✓。已选 1 条。
- (A,B)=2:加入。目前选了 {(B,C), (A,B)}。A 连 B,B 连 C,无回路 ✓。已选 2 条。
- (A,C)=3:如果加入,A-B-C-A 形成回路 ✗。跳过。
- (D,E)=3:加入。目前选了 {(B,C), (A,B), (D,E)}。无回路 ✓。已选 3 条。
- (B,D)=4:加入。目前选了 {(B,C), (A,B), (D,E), (B,D)}。B 连 D,D 连 E,无回路 ✓。已选 4 条 = n-1。停止。
最小生成树:{(B,C), (A,B), (D,E), (B,D)},总权值 = 1+2+3+4 = 10。
1 | 最小生成树: |
例题 3:霍夫曼编码
字符及频率:A=45, B=13, C=12, D=16, E=9, F=5。构造霍夫曼编码。
整体分析
按照霍夫曼算法,每次合并最小的两个节点。
详解
初始:叶子节点 A(45), B(13), C(12), D(16), E(9), F(5)
第1轮:选最小的两个 F(5) 和 E(9),合并为 FE(14)
1 | 节点池:A(45), B(13), C(12), D(16), FE(14) |
第2轮:选最小的两个 C(12) 和 FE(14),合并为 CFE(26)
1 | 节点池:A(45), B(13), D(16), CFE(26) |
第3轮:选最小的两个 B(13) 和 D(16),合并为 BD(29)
1 | 节点池:A(45), CFE(26), BD(29) |
第4轮:选最小的两个 CFE(26) 和 BD(29),合并为 BDCF(55)
1 | 节点池:A(45), BDCF(55) |
第5轮:合并最后两个 A(45) 和 BDCF(55),得到根节点(100)
最终的霍夫曼树:
1 | (100) |
编码(左=0,右=1):
- A: 0(1位)
- B: 100(3位)
- D: 101(3位)
- C: 110(3位)
- F: 1110(4位)
- E: 1111(4位)
验证:频率最高的 A 用最短的编码(1位),频率最低的 F 和 E 用最长的编码(4位)。✓
平均编码长度 = 45×1 + 13×3 + 16×3 + 12×3 + 5×4 + 9×4 = 45+39+48+36+20+36 = 224 位(每100个字符)。
本章速查表
| 概念 | 定义 | 一句话解释 |
|---|---|---|
| 无向树 | 连通且无回路的图 | “没有圈的连通图” |
| 森林 | 无回路的图(不一定连通) | “几棵独立的树” |
| 叶子 | 度为1的顶点 | “只连一条边的顶点” |
| 树的边数 | |E| = |V| - 1 | “n个顶点,n-1条边” |
| 非平凡树的叶子数 | ≥ 2 | “至少两个端点” |
| 生成树 | 包含所有顶点的树形子图 | “原图的骨架” |
| Kruskal算法 | 按权排序,贪心选边,不成回路就保留 | “先修最便宜的路” |
| Prim算法 | 从一点扩展,每次选最小跨边 | “滚雪球” |
| 根树 | 指定根的有向树 | “有方向的树” |
| 二叉树 | 每个节点最多2个子节点 | “左子和右子” |
| 满二叉树 | 每个非叶节点恰好2子,叶子同层 | “最满的二叉树” |
| 完全二叉树 | 除最后一层外全满,最后一层左连续 | “几乎满的二叉树” |
| 前缀码 | 无编码是另一编码的前缀 | “无歧义编码” |
| 霍夫曼编码 | 频率高用短码,用二叉树构造 | “最优压缩编码” |
第8章 组合分析初步
本章定位:从"有什么结构"到"有多少种"——计数是离散数学的另一个核心主题。前面几章讨论的都是"存在什么"、“是什么关系”,这一章要回答一个更实际的问题:某种东西到底有多少种?
驱动问题
17世纪,法国贵族德梅雷爵士遇到了一个赌博难题。 他和朋友赌掷骰子,约定先赢10局的人拿走全部赌注。当德梅雷赢了8局、朋友赢了7局时,赌博被迫中断。赌注该怎么分?德梅雷请教了数学家帕斯卡,帕斯卡又写信给费马讨论。这次通信催生了概率论——而概率论的基石,就是计数。
你每天都在用计数:
- 密码强度:一个6位纯数字密码,每一位可以是0到9共10个数字,总共有多少种可能?如果你每秒能试1万个密码,破解需要多久?
- 扑克牌概率:从一副52张扑克牌中随机发5张,拿到"同花顺"(5张同花色且点数连续)的概率是多少?拿到"一对"的概率又是多少?
- 路由选择:互联网中,从北京到上海有多少条不同的数据传输路径?如果某些线路故障,还能找到替代路径吗?
- 排课问题:10门课、5个时间段,有多少种排法使得没有冲突?
这些问题的共同点是:不是问"能不能",而是问"有多少"。要回答"有多少",我们需要系统化的计数工具。
8.1 加法法则与乘法法则
在学习排列组合之前,我们需要先掌握两个最基本的计数原理。它们是所有计数问题的基础,就像加法和乘法是所有算术的基础一样。
8.1.1 加法法则
加法法则(Sum Rule,也叫分类计数原理):如果完成一件事有 k 类不同的方案,第1类有 n₁ 种方法,第2类有 n₂ 种方法,……,第 k 类有 nₖ 种方法,而且这些方案之间互不重叠(选了这一类就不能同时选另一类),那么完成这件事的总方法数为:
直觉类比:想象你要从学校回家。你可以坐公交车(有3条线路),也可以骑共享单车(只有一种骑法),也可以走路(只有一条路)。这三种方式是互相排斥的——你不可能同时既坐公交又骑车。所以总共有 3 + 1 + 1 = 5 种回家方式。
关键词解释:
- 互斥(Mutually Exclusive):意思是"不能同时发生"。坐公交和骑车是互斥的,因为你一次只能选一种方式。如果两类方案可以同时选,就不能用加法。
什么时候用加法法则? 当你能把问题分成几个互不重叠的类别,而且你想知道"总共有多少种选择"的时候。
例子:一个班有30个男生和25个女生。从中选一个人当班长,有多少种选法?
分析:选班长这件事可以分成两类——选男生(30种)或选女生(25种),这两类互斥。所以总选法 = 30 + 25 = 55 种。
反例(不能用加法的情况):从一副牌中选一张红色的牌,或者选一张人脸牌(J、Q、K)。红色牌有26张,人脸牌有12张,但其中有6张既是红色又是人脸(红心和方块的J、Q、K)。这两类不互斥,直接相加 26 + 12 = 38 就多算了,需要减去重复的6张,实际是 26 + 12 - 6 = 32 张。
8.1.2 乘法法则
乘法法则(Product Rule,也叫分步计数原理):如果完成一件事需要 k 个连续的步骤,第1步有 n₁ 种方法,第2步有 n₂ 种方法,……,第 k 步有 nₖ 种方法,而且每一步的选择不受其他步骤的影响(即各步之间相互独立),那么完成这件事的总方法数为:
直觉类比:想象你要搭配一套衣服。你有3件上衣和4条裤子。选择上衣和选择裤子是两个独立的步骤——不管你选了哪件上衣,都可以搭配任意一条裤子。所以总共有 3 × 4 = 12 种搭配方式。
你可以想象成一棵"决策树":第一步选上衣有3个分支,每个分支下第二步选裤子又有4个分支,总共有 3 × 4 = 12 个叶子节点,对应12种搭配。
关键词解释:
- 独立(Independent):意思是"一个选择不影响另一个选择"。选上衣不会限制你选哪条裤子,所以这两步是独立的。如果选了某件上衣后只能搭配某些裤子,那就不独立了,乘法法则不能直接用。
什么时候用乘法法则? 当你需要连续做多个决定,而且每一步的选择都独立于其他步骤的时候。
例子:设置一个两位密码,第一位是大写字母(A-Z共26个),第二位是数字(0-9共10个)。总共有多少种密码?
分析:第一步选字母有26种,第二步选数字有10种,两步独立。总密码数 = 26 × 10 = 260 种。
8.1.3 加法法则与乘法法则的对比
| 特征 | 加法法则 | 乘法法则 |
|---|---|---|
| 核心思想 | 分类讨论 | 分步完成 |
| 关系 | 各类之间互斥(或) | 各步之间独立(且) |
| 运算 | 相加 | 相乘 |
| 关键词 | “要么……要么……” | “先……再……” |
| 例子 | 坐公交或骑车或走路 | 先选上衣再选裤子 |
一个综合例子:一个密码由一个字母(A-Z)后跟一个数字(0-9)或一个特殊字符(!@#$共4个)组成。总共有多少种密码?
分析:
- 第一步选字母:26种
- 第二步选后面的字符:可以是数字(10种)或特殊字符(4种),这是加法法则 → 10 + 4 = 14种
- 两步之间用乘法法则:26 × 14 = 364 种
这个例子展示了加法和乘法法则组合使用的情况。
8.2 排列与组合
加法法则和乘法法则解决的是"有多少种方式完成一件事"。但在实际问题中,我们经常需要从一堆东西中挑选一部分。这时候就涉及到两个核心概念:排列和组合。
在正式学习之前,我们需要先理解一个基础概念——阶乘。
8.2.1 阶乘
阶乘(Factorial),记作 n!,读作"n的阶乘",定义为从1到n所有正整数的乘积:
特别规定 0! = 1(这是一个约定,为了让公式在边界情况下也成立)。
直觉类比:阶乘回答的问题是"n个不同的东西排成一列,有多少种排法?"比如3个人(A、B、C)排成一排:第一个位置有3种选择,第二个位置有2种选择(剩下的人),第三个位置只有1种选择。所以 3! = 3 × 2 × 1 = 6 种排法。
常见阶乘值:
| n | n! | 计算过程 |
|---|---|---|
| 0 | 1 | 约定 |
| 1 | 1 | 1 |
| 2 | 2 | 2 × 1 |
| 3 | 6 | 3 × 2 × 1 |
| 4 | 24 | 4 × 3 × 2 × 1 |
| 5 | 120 | 5 × 4 × 3 × 2 × 1 |
| 6 | 720 | 6 × 5 × 4 × 3 × 2 × 1 |
| 10 | 3628800 | 约360万 |
阶乘的增长速度极快:10! 已经超过360万,20! 约为 2.4 × 10¹⁸,52!(一副扑克牌的排列数)约为 8 × 10⁶⁷——这个数字大得不可思议,比宇宙中原子的总数还多。
阶乘的递推关系:n! = n × (n-1)!。这个关系非常有用,可以简化计算。例如:5!/3! = (5 × 4 × 3!)/3! = 5 × 4 = 20。
8.2.2 排列
排列(Permutation):从 n 个不同的元素中,取出 r 个(r ≤ n),按照一定的顺序排成一列。排列关心的是"选了哪些"以及"它们的顺序是什么"。
记作 P(n, r),也可以写作 P_n^r 或 A(n, r),有些教材也记作 A_n^r。
直觉类比:想象一个班级有10个学生,要从中选出3个人分别担任班长、副班长、学习委员。这三个职位是不同的,所以选"张三当班长、李四当副班长、王五当学习委员"和选"李四当班长、张三当副班长、王五当学习委员"是两种不同的方案。这就是排列——顺序很重要。
公式推导(一步一步来):
从 n 个不同元素中取 r 个排列,可以分成 r 个步骤:
- 第1个位置:有 n 种选择
- 第2个位置:已经用了1个,剩下 n-1 种选择
- 第3个位置:已经用了2个,剩下 n-2 种选择
- ……
- 第 r 个位置:已经用了 r-1 个,剩下 n-(r-1) = n-r+1 种选择
由乘法法则:
用阶乘表示(分子分母同时除以 (n-r)!):
验证:P(10, 3) = 10!/(10-3)! = 10!/7! = 10 × 9 × 8 = 720。也就是说,从10人中选3人分别当班长、副班长、学习委员,有720种方案。
特殊情况:
- P(n, n) = n!/0! = n!/1 = n!:n个不同元素的全排列,就是把它们全部排成一列。
- P(n, 1) = n!/(n-1)! = n:从n个元素中取1个,当然就是n种选择。
- P(n, 0) = n!/n! = 1:从n个元素中取0个,只有"什么都不取"这一种方式。
8.2.3 组合
组合(Combination):从 n 个不同的元素中,取出 r 个(r ≤ n),不考虑它们的顺序。组合只关心"选了哪些",不关心"它们的排列顺序"。
记作 C(n, r),也可以写作 C_n^r 或 (读作"n choose r",即"n中选r")。
直觉类比:还是那个班级有10个学生,但这次不是选职位,而是选3个人组成一个委员会。委员会里没有职位之分,选"张三、李四、王五"和选"李四、张三、王五"是同一个委员会。这就是组合——顺序不重要。
排列与组合的核心区别:
| 特征 | 排列 | 组合 |
|---|---|---|
| 关心顺序? | 是 | 否 |
| 比喻 | 选人+排队(当不同职位) | 选人不排队(当同级委员) |
| 数值关系 | P(n,r) ≥ C(n,r) | C(n,r) ≤ P(n,r) |
| 公式 | n!/(n-r)! | n!/(r!(n-r)!) |
组合公式的直觉推导:
我们已经知道排列数 P(n, r) 表示"从n个中取r个并排列"。现在考虑组合——不考虑顺序。
关键思想:每一种组合,对应 r! 种排列。
为什么?假设我们选出了 r 个元素(比如 {A, B, C}),这 r 个元素可以自己内部排列成 r! 种不同的顺序。而排列 P(n, r) 把所有这些顺序都算进去了,组合 C(n, r) 只算一次。
所以:
验证:C(10, 3) = 10!/(3! × 7!) = 720/6 = 120。也就是说,从10人中选3人组成委员会,有120种方案(而选3人当不同职位有720种,720/6=120)。
r! 的含义:选出的3个人内部有 3! = 6 种排列方式,所以排列数是组合数的6倍。
8.2.4 组合的重要性质
性质1:对称性
直觉解释:从10个人中选3个人进入委员会,等价于从10个人中选7个人不进入委员会。选谁留下和选谁离开,是同一件事的两面。
验证:C(10, 3) = 120,C(10, 7) = 10!/(7!×3!) = 120 ✓
这个性质在计算时很有用:如果 r 比较大(比如 C(100, 97)),可以换成 C(100, 3) 来算,简单得多。
性质2:帕斯卡恒等式(递推关系)
直觉解释:想象你要从 n 个人中选 r 个人组成委员会。现在固定考虑其中某一个人(比如张三),只有两种情况:
- 情况A:张三在委员会中。那你还需要从剩下 n-1 个人中再选 r-1 个人,有 C(n-1, r-1) 种方式。
- 情况B:张三不在委员会中。那你需要从剩下 n-1 个人中选 r 个人,有 C(n-1, r) 种方式。
这两种情况互斥且覆盖所有可能(张三要么在要么不在),所以由加法法则,总数 = C(n-1, r-1) + C(n-1, r)。
帕斯卡三角(杨辉三角):这个递推关系恰好构成了帕斯卡三角。每一行的数字是组合数 C(n, r),每个数等于它"肩膀上"两个数之和。
1 | n=0: 1 |
验证第4行:C(4,0)=1, C(4,1)=4, C(4,2)=6, C(4,3)=4, C(4,4)=1。
验证递推:C(4,2) = C(3,1) + C(3,2) = 3 + 3 = 6 ✓
性质3:二项式定理
直觉解释:从 n 个人中选出一个子集(可以是任意大小),总共有多少种选法?每个人都有两种状态——被选中或不被选中。n 个人独立选择,由乘法法则,总共有 2ⁿ 种子集。另一方面,大小为 r 的子集有 C(n, r) 个,r 从0到n求和,就得到所有子集的总数。
一个等价的表述:这个性质来自二项式定理 。
8.2.5 多重集排列
前面讨论的排列,前提是"n个不同的元素"。但现实中,元素可能有重复。这就引出了多重集的概念。
多重集(Multiset):与普通集合不同,多重集允许元素重复出现。例如 {A, A, B, C, C, C} 就是一个多重集,其中 A 出现2次,B 出现1次,C 出现3次。
直觉类比:普通集合像是一堆不同的书(每本都不一样),多重集像是一堆可能相同的物品(比如一副扑克牌中有多张相同的牌——虽然花色不同,但如果只看点数,就有重复)。
多重集的排列:如果一个多重集有 n 个元素,其中有 k 种不同的元素,第 i 种元素出现了 nᵢ 次(n₁ + n₂ + … + nₖ = n),那么这 n 个元素的不同排列数为:
直觉推导:如果所有 n 个元素都不同,排列数就是 n!。但因为有些元素是相同的,交换两个相同元素不会产生新的排列。第1种元素有 n₁ 个,它们内部交换有 n₁! 种方式,但这些交换不产生新排列;同理第2种元素有 n₂! 种内部交换……所以要除以所有这些"无效交换"的数目。
经典例子:单词 MISSISSIPPI 的字母排列数。
分析:
- 总共有11个字母
- M 出现 1 次
- I 出现 4 次
- S 出现 4 次
- P 出现 2 次
排列数 = 11! / (1! × 4! × 4! × 2!)
计算过程:
- 11! = 39916800
- 1! = 1
- 4! = 24
- 4! = 24
- 2! = 2
- 分母 = 1 × 24 × 24 × 2 = 1152
- 结果 = 39916800 / 1152 = 34650
验证思路:如果我们把11个字母都看成不同的(比如给每个字母标上编号),排列数就是11!。但实际上,4个I之间互换(4!种方式)不会产生新的单词排列,4个S之间互换(4!种方式)也不会,2个P之间互换(2!种方式)也不会。所以要除以这些重复。
8.2.6 圆排列
圆排列(Circular Permutation):n 个不同元素围成一个圆圈的排列方式。
直觉解释:在普通的线性排列中,{A, B, C} 和 {B, C, A} 是不同的排列(A在第一个位置 vs B在第一个位置)。但在圆排列中,所有元素围成一圈,旋转视为相同——{A, B, C} 顺时针转一个位置变成 {B, C, A},它们在圆上是一样的。
为什么是 (n-1)! 而不是 n!?
n 个元素的线性排列有 n! 种。但在圆排列中,每个圆圈可以旋转出 n 种不同的线性排列(固定一个参考位置,顺时针看有 n 种起点)。这些旋转都是同一个圆排列,所以要除以 n:
具体例子:5个人围坐一张圆桌,有多少种坐法?
答案:(5-1)! = 4! = 24 种。
注意:如果圆桌有"特殊位置"(比如有一个是主座),那就不是圆排列了,而是普通的线性排列 P(5,5) = 5! = 120 种。圆排列的关键是没有固定参考点,旋转等价。
翻转也等价的情况:如果圆圈可以翻转(比如一串珠子项链,正面和反面一样),那么排列数还要再除以2:
8.2.7 排列组合公式汇总
| 概念 | 公式 | 含义 |
|---|---|---|
| 阶乘 | n! = n × (n-1) × … × 2 × 1 | n个不同元素的全排列数 |
| 排列 | P(n,r) = n!/(n-r)! | 从n个中取r个,考虑顺序 |
| 组合 | C(n,r) = n!/(r!(n-r)!) | 从n个中取r个,不考虑顺序 |
| 多重集排列 | n!/(n₁!n₂!…nₖ!) | 有重复元素的排列数 |
| 圆排列 | (n-1)! | n个元素围成圈的排列数 |
8.3 递推方程
8.3.1 什么是递推?
在前面的学习中,我们用排列组合公式直接计算结果。但有些问题太复杂,无法直接套公式。这时候,我们需要换一种思路:找到一个规律,用前面的结果来计算后面的结果。
递推(Recurrence):用前面的项来定义后面的项。就像多米诺骨牌——第一张倒下后,每一张都会推倒下一张。
递推方程(Recurrence Relation,也叫递推关系):一个等式,它定义了序列中的每一项与前面若干项之间的关系。
直觉类比:想象你在爬楼梯,每次可以走1级或2级。问"走到第n级有多少种走法?"你没法直接算,但可以发现规律:走到第n级,要么从第n-1级走1步上来,要么从第n-2级走2步上来。所以走到第n级的走法数 = 走到第n-1级的走法数 + 走到第n-2级的走法数。这就是一个递推关系。
递推方程需要两样东西:
- 递推关系:a(n) 与 a(n-1), a(n-2), … 之间的等式
- 初始条件(Base Cases):最前面几项的具体值(否则递推无从开始)
经典例子——斐波那契数列:
斐波那契数列(Fibonacci Sequence)的递推定义:
- 初始条件:F(0) = 0, F(1) = 1
- 递推关系:F(n) = F(n-1) + F(n-2)(n ≥ 2)
逐项计算:
- F(0) = 0
- F(1) = 1
- F(2) = F(1) + F(0) = 1 + 0 = 1
- F(3) = F(2) + F(1) = 1 + 1 = 2
- F(4) = F(3) + F(2) = 2 + 1 = 3
- F(5) = F(4) + F(3) = 3 + 2 = 5
- F(6) = F(5) + F(4) = 5 + 3 = 8
数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …
现实中的斐波那契:兔子繁殖问题(每月生一对新兔子,新生兔子第二个月开始繁殖)、向日葵种子的螺旋排列、贝壳的螺旋曲线——自然界中到处都有斐波那契数列的影子。
8.3.2 求解递推方程的方法一:迭代法
迭代法(Iteration,也叫展开法、前向替换法):反复把递推关系代入自身,直到出现初始条件,从而找到通项公式。
步骤:
- 写出递推关系
- 把递推关系中的 a(n-1) 用 a(n-2) 和 a(n-3) 表示,再代入
- 继续代入,直到看出规律
- 用初始条件验证
例题:求解 a(n) = 2a(n-1) + 1,a(0) = 1。
详解(一步一步展开):
第1步:写出前几项,观察规律。
- a(0) = 1
- a(1) = 2 × 1 + 1 = 3
- a(2) = 2 × 3 + 1 = 7
- a(3) = 2 × 7 + 1 = 15
- a(4) = 2 × 15 + 1 = 31
观察:1, 3, 7, 15, 31… 这些数都是 2 的幂减1:2¹-1, 2²-1, 2³-1, 2⁴-1, 2⁵-1…
猜测:a(n) = 2^(n+1) - 1
第2步:用迭代法推导。
把 a(n-1) = 2a(n-2) + 1 代入:
把 a(n-2) = 2a(n-3) + 1 代入:
看出规律了吗?
当 k = n 时(用到初始条件 a(0) = 1):
第3步:验证。a(3) = 2⁴ - 1 = 16 - 1 = 15 ✓
迭代法的优缺点:
- 优点:直观,容易理解
- 缺点:需要能看出规律,对于复杂递推可能不容易
8.3.3 求解递推方程的方法二:特征根法
迭代法靠的是"看出规律",但有些递推关系看规律很困难。特征根法(Characteristic Root Method)提供了一种系统化的、有固定步骤的求解方法。
适用条件:常系数线性齐次递推方程。我们来解释这些术语:
- 常系数:系数是常数(不随n变化)。比如 a(n) = 5a(n-1) - 6a(n-2),系数5和-6都是常数。
- 线性:每一项都是 a(n), a(n-1), a(n-2), … 的一次方,没有 a(n-1)² 这样的项。
- 齐次:等式右边没有常数项。比如 a(n) = 2a(n-1) + 1 就不是齐次的(因为有+1),而 a(n) = 2a(n-1) 是齐次的。
- 递推方程:用前面的项定义后面的项的等式。
情况1:两个不同的特征根
对于递推方程 a(n) = c₁·a(n-1) + c₂·a(n-2),其中 c₁、c₂ 是常数。
完整步骤:
第1步:写出特征方程
把 a(n) = xⁿ 代入递推方程(假设解的形式是指数函数),得到:
两边除以 x^(n-2):
这就是特征方程。整理成标准形式:
第2步:求特征根
用求根公式或因式分解,解出特征方程的两个根 r₁ 和 r₂。
第3步:写出通解
如果两个根不相等(r₁ ≠ r₂),通解为:
其中 α 和 β 是待定常数。
第4步:代入初始条件,确定 α 和 β
把 n=0 和 n=1 的值代入通解,得到两个方程,解出 α 和 β。
完整例题
题目:求解 a(n) = 5a(n-1) - 6a(n-2),a(0) = 1,a(1) = 2。
第1步:写特征方程
把 a(n) = xⁿ 代入:
两边除以 x^(n-2):
整理:
第2步:求特征根
因式分解:(x - 2)(x - 3) = 0
特征根:r₁ = 2,r₂ = 3(两个不同的根)
第3步:写通解
第4步:代入初始条件
当 n = 0 时:a(0) = α · 2⁰ + β · 3⁰ = α + β = 1 … ①
当 n = 1 时:a(1) = α · 2¹ + β · 3¹ = 2α + 3β = 2 … ②
由①得 α = 1 - β,代入②:
2(1 - β) + 3β = 2
2 - 2β + 3β = 2
β = 0
所以 α = 1 - 0 = 1。
最终答案:a(n) = 1 · 2ⁿ + 0 · 3ⁿ = 2ⁿ
验证:
- a(0) = 2⁰ = 1 ✓
- a(1) = 2¹ = 2 ✓
- a(2) = 5a(1) - 6a(0) = 5×2 - 6×1 = 4 = 2² ✓
- a(3) = 5a(2) - 6a(1) = 5×4 - 6×2 = 8 = 2³ ✓
情况2:两个相同的特征根
如果特征方程有重根(r₁ = r₂ = r),那么 α·rⁿ + β·rⁿ = (α+β)·rⁿ 实际上只有一个自由参数,无法满足两个初始条件。
解决办法:通解的形式要修改为:
例题:求解 a(n) = 4a(n-1) - 4a(n-2),a(0) = 1,a(1) = 3。
第1步:特征方程 x² = 4x - 4,即 x² - 4x + 4 = 0,即 (x-2)² = 0。
第2步:重根 r = 2。
第3步:通解 a(n) = (α + βn) · 2ⁿ。
第4步:代入初始条件:
- a(0) = (α + 0) · 1 = α = 1
- a(1) = (1 + β) · 2 = 3 → β = 1/2
答案:a(n) = (1 + n/2) · 2ⁿ = 2ⁿ + n · 2^(n-1)
验证:a(2) = (1 + 1) · 4 = 8,而 4a(1) - 4a(0) = 12 - 4 = 8 ✓
8.3.4 特征根法总结
| 步骤 | 操作 | 示例(a(n)=5a(n-1)-6a(n-2)) |
|---|---|---|
| 1. 写特征方程 | 将 a(n)=xⁿ 代入,约去公因子 | x² - 5x + 6 = 0 |
| 2. 求根 | 因式分解或求根公式 | r₁=2, r₂=3 |
| 3. 写通解 | 两根不同:αr₁ⁿ+βr₂ⁿ;重根:(α+βn)rⁿ | a(n) = α·2ⁿ + β·3ⁿ |
| 4. 代入初始条件 | 用 a(0) 和 a(1) 列方程组,解出 α, β | α=1, β=0 |
特征根法的直觉:为什么假设解是 xⁿ 的形式?因为指数函数 a(n) = rⁿ 满足递推关系 a(n) = c₁·a(n-1) + c₂·a(n-2)(即 rⁿ = c₁·r^(n-1) + c₂·r^(n-2),约去 r^(n-2) 就是特征方程)。由于递推是线性的,两个解的线性组合也是解(叠加原理),所以通解是 α·r₁ⁿ + β·r₂ⁿ。
例题与详解
例题1:扑克牌概率
从52张扑克牌中随机发5张,求拿到"一对"的概率。
(解释:扑克牌有52张,分为4种花色(黑桃、红心、方块、梅花),每种花色13张(A, 2, 3, …, 10, J, Q, K)。"一对"是指5张牌中恰好有2张点数相同,其余3张的点数各不相同,且与那2张的点数也不同。)
整体分析
概率 = "一对"的组合数 / 所有可能的5张牌组合数。
因为发牌不考虑顺序(5张牌同时拿到,没有"第一张""第二张"之分),所以用组合。
详解
第1步:计算总组合数
从52张牌中选5张(不考虑顺序):
第2步:计算"一对"的组合数
分4个步骤(用乘法法则,因为每步独立):
步骤A:选择"一对"的点数
一副牌有13种点数(A, 2, 3, …, 10, J, Q, K),选1种作为"一对"的点数:
步骤B:从该点数的4张牌中选2张
每种点数有4张(4种花色各一张),选2张组成"一对":
步骤C:选择剩下3张牌的点数
剩下12种点数可选(排除已选的那一种),从中选3种不同的点数:
步骤D:为这3种点数各选1张花色
每种点数有4张牌(4种花色),选1张。3种点数各选1张:
第3步:用乘法法则
一对的组合数 = 13 × 6 × 220 × 64 = 1098240
逐步计算:13 × 6 = 78,78 × 220 = 17160,17160 × 64 = 1098240。
第4步:计算概率
结论:发5张牌,拿到一对的概率约为42.26%——这是最常见的牌型。
思考:为什么不能用排列?因为发牌只看"拿到了哪些牌",不看"先拿到哪张后拿到哪张"。5张牌拿到手后,顺序无关。如果题目问"第一张是什么第二张是什么",那就用排列。
例题2:递推方程——二进制串计数
题目:一个 n 位的二进制串(每一位是0或1的字符串),要求不包含连续两个1。求合法的 n 位二进制串有多少个。
整体分析
直接计数很困难(需要枚举所有 2ⁿ 种串然后筛选)。但我们可以用递推的思想:把大问题分解成小问题。
关键洞察:根据最后一位分类讨论。
详解
第1步:定义
设 a(n) = n 位合法二进制串的个数。
第2步:找递推关系
考虑一个合法的 n 位串,看它的最后一位:
情况A:最后一位是 0
那么前 n-1 位只要是一个合法的 (n-1) 位串就行(因为最后是0,不会产生连续两个1)。有 a(n-1) 种。
情况B:最后一位是 1
那倒数第二位必须是 0(否则最后两位就是"11",违反规则)。
所以最后两位是 “01”,前 n-2 位只要是一个合法的 (n-2) 位串就行。有 a(n-2) 种。
由加法法则(两种情况互斥且覆盖所有可能):
第3步:确定初始条件
- a(1) = 2:1位串可以是 “0” 或 “1”,都合法。
- a(2) = 3:2位串有4种(00, 01, 10, 11),其中"11"不合法,剩下3种。
第4步:求解
递推关系 a(n) = a(n-1) + a(n-2),a(1) = 2,a(2) = 3——这正是斐波那契数列!
逐项计算:
- a(1) = 2
- a(2) = 3
- a(3) = a(2) + a(1) = 3 + 2 = 5
- a(4) = a(3) + a(2) = 5 + 3 = 8
- a(5) = a(4) + a(3) = 8 + 5 = 13
数列:2, 3, 5, 8, 13, 21, 34, …(从第3项开始的斐波那契数列)
第5步:验证
a(3) = 5,枚举所有3位合法串:000, 001, 010, 100, 101。确实5个 ✓
(排除的有:011(末尾连续11), 110(开头连续11), 111(连续11))
第6步:用特征根法求通解
特征方程:x² = x + 1,即 x² - x - 1 = 0
求根:x = (1 ± √5) / 2
r₁ = (1 + √5)/2 ≈ 1.618(黄金比例φ)
r₂ = (1 - √5)/2 ≈ -0.618
通解:a(n) = α·r₁ⁿ + β·r₂ⁿ
代入初始条件 a(1)=2, a(2)=3:
α·r₁ + β·r₂ = 2
α·r₁² + β·r₂² = 3
解这个方程组(过程略),得到 α 和 β 的值,最终得到 a(n) 的封闭公式。
不过对于斐波那契数列,我们通常直接用递推关系或查表,不需要每次都求通解。
例题3:综合应用——密码强度
题目:一个密码由 8 个字符组成,每个字符可以是大写字母(A-Z,26个)、小写字母(a-z,26个)或数字(0-9,10个)。总共有多少种不同的密码?如果要求密码中至少包含一个数字,又有多少种?
详解
第1步:无限制的密码总数
每个位置有 26 + 26 + 10 = 62 种选择。8个位置独立选择,由乘法法则:
约218万亿种。
第2步:至少包含一个数字
“至少包含一个数字"的反面是"完全不包含数字”(即全是字母)。
用补集法(间接法):
全是字母的密码数:每个位置只有 26 + 26 = 52 种选择。
至少包含一个数字的密码数 = 总数 - 全是字母的数:
约165万亿种。
结论:在所有8位密码中,约75.5%至少包含一个数字。这就是为什么很多网站要求密码必须包含数字——它排除了约25%的"弱密码"(纯字母密码)。
本章速查表
| 概念 | 公式 | 一句话解释 |
|---|---|---|
| 加法法则 | n₁ + n₂ + … + nₖ | 分类讨论,互斥方案相加 |
| 乘法法则 | n₁ × n₂ × … × nₖ | 分步完成,独立步骤相乘 |
| 阶乘 n! | n × (n-1) × … × 2 × 1 | n个不同元素排成一列的排列数;0!=1 |
| 排列 P(n,r) | n!/(n-r)! | 从n个中取r个,有顺序之分 |
| 组合 C(n,r) | n!/(r!(n-r)!) | 从n个中取r个,无顺序之分 |
| 组合对称性 | C(n,r) = C(n,n-r) | 选r个留下 = 选n-r个淘汰 |
| 帕斯卡恒等式 | C(n,r) = C(n-1,r-1) + C(n-1,r) | 固定某人"在或不在"分两类 |
| 子集总数 | C(n,0)+C(n,1)+…+C(n,n) = 2ⁿ | n个元素的所有子集数 |
| 多重集排列 | n!/(n₁!n₂!…nₖ!) | 有重复元素时,除以每种元素的内部排列数 |
| 圆排列 | (n-1)! | n个元素围成圈,旋转等价 |
| 递推方程 | 用前面的项定义后面的项 | 像多米诺骨牌,前项推后项 |
| 特征根法 | 特征方程→求根→通解→代入初值 | 求解常系数线性齐次递推的系统方法 |
| 特征方程 | x² = c₁x + c₂(即 x² - c₁x - c₂ = 0) | 把 a(n)=xⁿ 代入递推方程得到 |
| 两根不同的通解 | a(n) = αr₁ⁿ + βr₂ⁿ | 两个指数解的线性组合 |
| 重根的通解 | a(n) = (α+βn)rⁿ | 重根时多乘一个n |
| 斐波那契数列 | F(n) = F(n-1) + F(n-2) | 每项等于前两项之和 |
第9章 代数系统简介
本章定位:从具体到抽象——把前面学过的各种运算提炼成统一的代数框架。我们不再关心"这个数是多少",而是关心"这些元素之间怎样运算、运算有什么规律"。
驱动问题
魔方有 4.3×10^19 种状态,但为什么数学家说它"只有"一个群结构? 一个三阶魔方的所有旋转操作(顺时针转顶层、逆时针转前面……)构成一个群。群论可以告诉你:任何状态都可以用有限步还原。你不需要记住每种状态的还原方法——理解群的结构就够了。事实上,魔方群的直径(最远状态需要的最少步数)是20,这就是著名的"上帝之数"。
你网购时输入的信用卡号,为什么输错一位就能被检测出来? 校验码的数学原理来自模运算——一种代数系统。最后一位是前面数字经过特定运算得到的,输错任何一位都会导致校验失败。这背后用到的是模10算术中单位元和逆元的性质。
现代互联网安全的基石——RSA 加密算法,依赖的是整数模 n 乘法群的性质。RSA 的安全性基于一个事实:给定两个大素数 p 和 q,计算 n=p×q 很容易,但从 n 反推 p 和 q 极其困难。你每次登录网站、发送加密消息,都在用群论。
9.1 二元运算及其性质
什么是二元运算?
在日常生活中,我们一直在做"运算":两个数相加、两个矩阵相乘、两个集合取并集……这些操作有一个共同特征:拿两个输入,产生一个输出。
二元运算(Binary Operation)的形式定义:给定一个非空集合 S,一个从 S × S 到 S 的函数 ∘ 叫做 S 上的二元运算。用符号写就是:
1 | ∘ : S × S → S |
其中 S × S 是笛卡尔积(Cartesian Product),意思是"所有有序对 (a, b) 的集合,其中 a 和 b 都属于 S"。
直觉:二元运算就是一台机器,你往里面放两个同类型的材料,它吐出一个同类型的成品。关键要求是:放进去的东西和拿出来的东西必须是同一类。
正例:
- 整数加法:(Z, +)。3 + 5 = 8,两个整数进去,一个整数出来。
- 整数乘法:(Z, ×)。3 × 5 = 15,两个整数进去,一个整数出来。
- 集合并集:(P(S), ∪)。{1,2} ∪ {2,3} = {1,2,3},两个集合进去,一个集合出来。
- 矩阵乘法:(M_n®, ×)。两个 n 阶实矩阵相乘,结果还是 n 阶实矩阵。
反例:
- 整数除法:(Z, ÷)。3 ÷ 2 = 1.5,结果不是整数,所以除法不是整数集上的二元运算。它"跑出去了"。
- 自然数减法:(N, −)。3 − 5 = −2,结果不是自然数(如果自然数定义为 {0,1,2,…}),所以减法不是自然数集上的二元运算。
封闭性
封闭性(Closure):对于集合 S 上的二元运算 ∘,如果对任意 a, b ∈ S,都有 a ∘ b ∈ S,则称运算 ∘ 在 S 上是封闭的。
直觉:封闭性就是说"门关着,结果跑不出去"。你在这个集合里随便挑两个元素做运算,结果还在这个集合里。
为什么封闭性重要? 如果一个运算不封闭,你就没法在这个集合内部"自给自足"——每次运算都可能跑到集合外面去,这样你根本无法在这个集合上建立完整的代数结构。
正例:
- 偶数集上的加法:偶数 + 偶数 = 偶数。封闭。
- {0, 1, 2, 3, 4, 5} 上的模6加法:任意两个元素相加后对6取余,结果仍在 {0,1,2,3,4,5} 中。封闭。
反例:
- 正整数集上的减法:3 − 5 = −2,不是正整数。不封闭。
- 整数集上的除法:1 ÷ 2 = 0.5,不是整数。不封闭。
交换律
交换律(Commutativity):对于集合 S 上的二元运算 ∘,如果对任意 a, b ∈ S,都有:
1 | a ∘ b = b ∘ a |
则称运算 ∘ 满足交换律。
直觉:交换律就是说"顺序无所谓"。先做 a 再做 b,和先做 b 再做 a,结果一样。就像你穿衣服——先穿左袖再穿右袖,和先穿右袖再穿左袖,结果一样。
正例:
- 整数加法:3 + 5 = 5 + 3 = 8。满足交换律。
- 整数乘法:3 × 5 = 5 × 3 = 15。满足交换律。
- 集合并集:{1,2} ∪ {2,3} = {2,3} ∪ {1,2} = {1,2,3}。满足交换律。
反例:
- 整数减法:3 − 1 = 2,但 1 − 3 = −2。不满足交换律。这就是为什么减法"感觉"比加法复杂——你不能随便调换顺序。
- 矩阵乘法:一般来说 AB ≠ BA。矩阵乘法不满足交换律。
- 函数复合:f∘g 和 g∘f 通常不同。例如 f(x)=x+1, g(x)=2x,则 f(g(x))=2x+1,而 g(f(x))=2(x+1)=2x+2。
结合律
结合律(Associativity):对于集合 S 上的二元运算 ∘,如果对任意 a, b, c ∈ S,都有:
1 | (a ∘ b) ∘ c = a ∘ (b ∘ c) |
则称运算 ∘ 满足结合律。
直觉:结合律就是说"先算哪两个无所谓"。三个数连加,你先算前两个还是后两个,结果一样。这让你可以不加括号地写 a ∘ b ∘ c,而不产生歧义。
正例:
- 整数加法:(2 + 3) + 4 = 5 + 4 = 9,2 + (3 + 4) = 2 + 7 = 9。满足结合律。
- 整数乘法:(2 × 3) × 4 = 6 × 4 = 24,2 × (3 × 4) = 2 × 12 = 24。满足结合律。
反例:
- 整数减法:(5 − 3) − 1 = 2 − 1 = 1,但 5 − (3 − 1) = 5 − 2 = 3。不满足结合律。所以 a − b − c 这种写法是有歧义的,必须加括号。
- 实数的幂运算:(2^3)^2 = 8^2 = 64,但 2^(3^2) = 2^9 = 512。不满足结合律。
分配律
分配律(Distributivity):给定集合 S 上的两个二元运算 ∘ 和 ⊕,如果对任意 a, b, c ∈ S,都有:
1 | a ∘ (b ⊕ c) = (a ∘ b) ⊕ (a ∘ c) (左分配律) |
则称 ∘ 对 ⊕ 满足分配律。
直觉:分配律就是说"乘法可以拆开来算"。a × (b + c) = a×b + a×c。这和你在小学学的"拆括号"是一回事。分配律描述了两个运算之间的关系——一个运算如何"穿透"另一个运算。
正例:
- 整数乘法对加法分配:3 × (4 + 5) = 3 × 9 = 27,(3 × 4) + (3 × 5) = 12 + 15 = 27。满足分配律。
- 集合交对并分配:A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C)。满足分配律。
反例:
- 整数加法对乘法不分配:3 + (4 × 5) = 3 + 20 = 23,(3 + 4) × (3 + 5) = 7 × 8 = 56。不满足。所以分配律是有方向的——乘法对加法分配,但加法不对乘法分配。
单位元
单位元(Identity Element):对于集合 S 上的二元运算 ∘,如果存在一个元素 e ∈ S,使得对任意 a ∈ S,都有:
1 | a ∘ e = e ∘ a = a |
则称 e 是运算 ∘ 的单位元(也叫幺元)。
直觉:单位元就是"什么都不做"的元素。就像加0:a + 0 = a,0不会改变任何数。或者乘1:a × 1 = a,1不会改变任何数。单位元是运算的"静止点"。
具体例子:
- (Z, +) 的单位元是 0:a + 0 = 0 + a = a。
- (Z, ×) 的单位元是 1:a × 1 = 1 × a = a。
- (P(S), ∪) 的单位元是空集 ∅:A ∪ ∅ = ∅ ∪ A = A。
- (P(S), ∩) 的单位元是全集 S:A ∩ S = S ∩ A = A。
- (M_n®, 矩阵乘法) 的单位元是 n 阶单位矩阵 I_n(对角线上全是1,其余全是0)。
单位元是唯一的:一个运算最多只有一个单位元。
证明:假设 e₁ 和 e₂ 都是单位元。
- 因为 e₁ 是单位元,所以 e₁ ∘ e₂ = e₂(用 e₁ 的单位元性质)。
- 因为 e₂ 是单位元,所以 e₁ ∘ e₂ = e₁(用 e₂ 的单位元性质)。
- 因此 e₁ = e₁ ∘ e₂ = e₂。证毕。
这个证明的核心思想是:两个单位元互相作用,都"不改变"对方,所以它们必须相等。
逆元
逆元(Inverse Element):设运算 ∘ 有单位元 e。对于元素 a ∈ S,如果存在 b ∈ S 使得:
1 | a ∘ b = b ∘ a = e |
则称 b 是 a 的逆元,记作 a⁻¹。
直觉:逆元就是"撤销操作"。加法的逆元是取相反数:3 + (−3) = 0,−3 把 3 "撤销"了。乘法的逆元是取倒数:3 × (1/3) = 1,1/3 把 3 "撤销"了。
具体例子:
- (Z, +) 中,a 的逆元是 −a:3 + (−3) = 0。
- (R{0}, ×) 中,a 的逆元是 1/a:3 × (1/3) = 1。
- (Z₆, +₆) 中,a 的逆元是 6−a(对非零元):在模6加法中,2 的逆元是 4,因为 (2+4) mod 6 = 0。而 0 的逆元是 0 本身。
逆元是唯一的:每个元素最多只有一个逆元。
证明:假设 b 和 c 都是 a 的逆元。
- b = b ∘ e(e 是单位元)
- = b ∘ (a ∘ c)(因为 c 是 a 的逆元,所以 a ∘ c = e)
- = (b ∘ a) ∘ c(结合律)
- = e ∘ c(因为 b 是 a 的逆元,所以 b ∘ a = e)
- = c(e 是单位元)
- 所以 b = c。证毕。
这个证明的核心思想是:利用结合律,把 b 和 c “拉到一起”,通过中间的 a 和单位元来建立等式。
幂等律
幂等律(Idempotency):对于集合 S 上的二元运算 ∘,如果对任意 a ∈ S,都有:
1 | a ∘ a = a |
则称运算 ∘ 满足幂等律。
直觉:幂等律就是说"自己和自己运算,结果还是自己"。做一次和做多次效果一样。
正例:
- 集合并集:A ∪ A = A。
- 集合交集:A ∩ A = A。
- 取最大值:max(a, a) = a。
反例:
- 整数加法:3 + 3 = 6 ≠ 3。不满足幂等律。
- 整数乘法:3 × 3 = 9 ≠ 3。不满足幂等律(但 0 和 1 满足)。
小结:二元运算性质速查
| 性质 | 定义 | 直觉 | 正例 | 反例 |
|---|---|---|---|---|
| 封闭性 | a∘b ∈ S | 结果跑不出去 | 偶数+偶数=偶数 | 整数÷整数不一定是整数 |
| 交换律 | a∘b = b∘a | 顺序无所谓 | 3+5=5+3 | 3−1≠1−3 |
| 结合律 | (a∘b)∘c = a∘(b∘c) | 先算哪两个无所谓 | (2+3)+4=2+(3+4) | (5−3)−1≠5−(3−1) |
| 分配律 | a∘(b⊕c)=(a∘b)⊕(a∘c) | 可以拆括号 | 3×(4+5)=3×4+3×5 | 3+(4×5)≠(3+4)×(3+5) |
| 单位元 | ∃e, a∘e=e∘a=a | 什么都不做 | 加法的0,乘法的1 | 整数减法没有单位元 |
| 逆元 | ∃a⁻¹, a∘a⁻¹=e | 撤销操作 | 加法中a的逆元是−a | 整数乘法中2没有逆元 |
| 幂等律 | a∘a=a | 做一次等于做多次 | A∪A=A | 3+3≠3 |
9.2 代数系统
什么是代数系统?
前面我们讨论了各种运算和它们的性质。现在我们要把这些东西"打包"在一起。
代数系统(Algebraic System,也叫代数结构 Algebraic Structure):一个非空集合 S,配上 S 上的一个或多个二元运算,以及可能的一些特异元素(如单位元),就构成一个代数系统。记作 (S, ∘) 或 (S, ∘, ⊕) 等。
直觉:代数系统就是"一个团队"——有成员(集合 S),有工作方式(运算 ∘),有规章制度(运算满足的性质)。不同的代数系统就像不同类型的团队:有的纪律严明(群),有的比较松散(半群),有的有两个工种(环有两个运算)。
为什么要研究代数系统? 因为很多看似不同的数学结构(整数加法、矩阵乘法、几何变换……)在抽象层面上有相同的规律。如果你理解了一个抽象代数系统的性质,你就同时理解了所有具体实现。这就是抽象的力量——一次证明,处处适用。
同态
同态(Homomorphism):设 (S, ∘) 和 (T, *) 是两个代数系统。一个映射 f: S → T 如果满足以下条件:
1 | 对任意 a, b ∈ S:f(a ∘ b) = f(a) * f(b) |
则称 f 是一个从 (S, ∘) 到 (T, *) 的同态映射(简称同态)。
直觉:同态是一种"保持运算结构"的翻译。你可以把它想象成一种翻译器:把 S 中的元素翻译成 T 中的元素。关键要求是:先运算再翻译 = 先翻译再运算。换句话说,翻译不会破坏运算的关系。
为什么同态重要? 同态告诉你两个代数系统之间有"结构上的联系"。如果你在 S 中做了一个运算,你可以放心地"翻译"到 T 中去——结果是对应的。这在密码学、编码理论中有大量应用。
具体例子:
-
指数映射:f: (R, +) → (R⁺, ×),f(x) = eˣ。
验证:f(a + b) = e^(a+b) = eᵃ × eᵇ = f(a) × f(b)。✓
这个同态说明:实数加法和正实数乘法有相同的结构。加法中的"加"对应乘法中的"乘",加法中的0对应乘法中的1。
-
模n映射:f: (Z, +) → (Z_n, +_n),f(a) = a mod n。
验证:f(a + b) = (a+b) mod n = (a mod n) +_n (b mod n) = f(a) +_n f(b)。✓
这个同态说明:整数加法和模n加法有相同的结构。模n运算"折叠"了整数,但保留了加法的结构。
-
行列式映射:det: (M_n®, ×) → (R, ×),det(A) = A的行列式。
验证:det(AB) = det(A) × det(B)。✓
这个同态说明:矩阵乘法和实数乘法通过行列式联系在一起。
同态的性质:
- 同态把单位元映射到单位元:如果 e_S 是 S 的单位元,则 f(e_S) 是 T 的单位元。
- 同态把逆元映射到逆元:如果 a⁻¹ 是 a 的逆元,则 f(a⁻¹) 是 f(a) 的逆元。
同构
同构(Isomorphism):如果同态 f: S → T 同时是一个双射(既是单射又是满射),则称 f 是一个同构映射(简称同构)。
这里需要解释几个术语:
- 单射(Injective,也叫"一对一"):不同的输入映射到不同的输出。即 a ≠ b ⇒ f(a) ≠ f(b)。
- 满射(Surjective,也叫"映上"):T 中每个元素都被映射到。即对任意 t ∈ T,存在 s ∈ S 使得 f(s) = t。
- 双射(Bijective):既是单射又是满射。每个 S 中的元素对应一个唯一的 T 中的元素,反之亦然。
直觉:同构意味着两个代数系统"本质上相同"——只是元素的名称不同,结构完全一样。就像两个魔方,一个贴的是中文标签,一个贴的是英文标签,但旋转结构完全相同。同构是代数系统之间最强的等价关系。
如果两个系统同构,它们有什么共同点?
- 元素个数相同
- 运算表完全相同(只是行列标签换了)
- 所有代数性质都相同(交换律、结合律等)
例子:(Z_4, +_4) 和 ({1, i, −1, −i}, ×) 同构。映射为 f(0)=1, f(1)=i, f(2)=−1, f(3)=−i。你可以验证:模4加法的运算表和这四个虚数单位的乘法表完全对应。
子代数
子代数(Subalgebra):设 (S, ∘) 是一个代数系统,T 是 S 的非空子集。如果 T 对运算 ∘ 封闭(即对任意 a, b ∈ T,都有 a ∘ b ∈ T),则 (T, ∘) 是 (S, ∘) 的子代数。
直觉:子代数就是"团队中的小团队"——这个小团队的成员在内部做运算时,结果不会跑到外面去。它是大代数系统的"缩小版",继承了同样的运算规则。
具体例子:
- (偶数集, +) 是 (Z, +) 的子代数。因为偶数 + 偶数 = 偶数。
- ({0, 2, 4}, +₆) 是 (Z₆, +₆) 的子代数。因为 0+6=0, 0+2=2, 0+4=4, 2+2=4, 2+4=0, 4+4=2,所有结果仍在 {0,2,4} 中。
- ({1}, ×) 是 (R{0}, ×) 的子代数(虽然很小,但 1 × 1 = 1,封闭)。
不是子代数的例子:
- (正整数集, −) 不是 (Z, −) 的子代数。因为 3 − 5 = −2,不在正整数集中。
9.3 典型代数系统
现在我们来看几种最重要的代数系统。它们按"条件越来越严格"排列——从最松散的半群到最严格的域。
半群
半群(Semigroup):一个代数系统 (S, ∘),其中运算 ∘ 满足:
- 封闭性:对任意 a, b ∈ S,a ∘ b ∈ S。
- 结合律:对任意 a, b, c ∈ S,(a ∘ b) ∘ c = a ∘ (b ∘ c)。
直觉:半群是最基本的代数系统——只要有封闭性和结合律就行。它不需要单位元,也不需要逆元。半群就像一个"只能前进、不能后退"的系统——你可以反复做运算,但不能撤销。
具体例子:
- (N, +) 是半群。自然数加法封闭且满足结合律,但没有加法逆元(3 的逆元 −3 不是自然数)。
- (字符串集合, 拼接) 是半群。两个字符串拼接后还是字符串(封闭),拼接满足结合律 (“ab”+“c”)+“d” = “ab”+(“c”+“d”) = “abcd”。但没有"逆拼接"这回事。
- (正整数集, ×) 是半群。
不是半群的例子:
- (Z, −) 不是半群。减法不满足结合律:(5−3)−1 ≠ 5−(3−1)。
幺半群
幺半群(Monoid):一个半群 (S, ∘),如果还存在单位元 e(即对任意 a ∈ S,a ∘ e = e ∘ a = a),则称为幺半群。
直觉:幺半群 = 半群 + 单位元。它是"可以原地踏步,但仍然不能后退"的系统——你有"什么都不做"的选项,但还是不能撤销。
具体例子:
- (N, +) 是幺半群,单位元是 0。(有些教材定义自然数不含0,此时 (N∪{0}, +) 是幺半群。)
- (字符串集合 ∪ {空串}, 拼接) 是幺半群,单位元是空串。
- (Z, ×) 是幺半群,单位元是 1。
群
群(Group):一个代数系统 (G, ∘),满足以下四个条件:
- 封闭性:对任意 a, b ∈ G,a ∘ b ∈ G。
- 结合律:对任意 a, b, c ∈ G,(a ∘ b) ∘ c = a ∘ (b ∘ c)。
- 单位元:存在 e ∈ G,使得对任意 a ∈ G,a ∘ e = e ∘ a = a。
- 逆元:对任意 a ∈ G,存在 a⁻¹ ∈ G,使得 a ∘ a⁻¹ = a⁻¹ ∘ a = e。
直觉:群是"可以做运算、可以撤销"的代数系统。你不仅有"什么都不做"的选项(单位元),还能"撤销任何操作"(逆元)。群是数学中最重要的代数结构之一——它完美地描述了"对称性"的概念。
逐一检查群的四个条件(以整数加法群 (Z, +) 为例):
- 封闭性:整数 + 整数 = 整数。✓
- 结合律:(a+b)+c = a+(b+c)。✓
- 单位元:0,因为 a+0 = 0+a = a。✓
- 逆元:a 的逆元是 −a,因为 a+(−a) = 0。✓
所以 (Z, +) 是群。
群的经典例子:
| 群 | 集合 | 运算 | 单位元 | 逆元 |
|---|---|---|---|---|
| 整数加法群 | Z | + | 0 | −a |
| 非零实数乘法群 | R{0} | × | 1 | 1/a |
| 模n加法群 | {0,1,…,n−1} | +_n | 0 | n−a |
| 模n乘法群 | {与n互质的数} | ×_n | 1 | 由扩展欧几里得算法求 |
群的性质(可以从四个公理推导出来):
- 单位元唯一:群中只有一个单位元。(证明见9.1节)
- 逆元唯一:每个元素只有一个逆元。(证明见9.1节)
- 消去律:如果 a ∘ b = a ∘ c,则 b = c。证明:两边同时左乘 a⁻¹,得 a⁻¹ ∘ (a ∘ b) = a⁻¹ ∘ (a ∘ c),由结合律得 (a⁻¹ ∘ a) ∘ b = (a⁻¹ ∘ a) ∘ c,即 e ∘ b = e ∘ c,即 b = c。
- (a∘b)⁻¹ = b⁻¹∘a⁻¹:两个元素之积的逆元,等于逆元之积(顺序反过来)。这和"穿衣服脱衣服"的直觉一样——先穿袜子再穿鞋,脱的时候要先脱鞋再脱袜子。
特殊类型的群
阿贝尔群(Abel Group,也叫交换群 Commutative Group):满足交换律的群。即除了群的四个条件外,还满足 a ∘ b = b ∘ a。
直觉:阿贝尔群是"完全民主"的群——谁先谁后无所谓。整数加法群是阿贝尔群,但矩阵乘法群不是(AB ≠ BA)。
循环群(Cyclic Group):如果群 G 中存在一个元素 g(称为生成元),使得 G 中每个元素都可以表示为 g 的某个幂(即 g^n 的形式,n 为整数),则称 G 是循环群。
直觉:循环群是"一个人就能撑起整个团队"的群——一个生成元通过反复"自我运算"就能产生群中所有元素。就像一个时钟,时针转一圈就能经过所有时间。
具体例子:
- (Z_n, +_n) 是循环群,生成元是 1。因为 1, 1+1=2, 1+1+1=3, …, 就能生成所有元素。
- ({1, −1, i, −i}, ×) 是循环群,生成元是 i。因为 i¹=i, i²=−1, i³=−i, i⁴=1。
环
环(Ring):一个代数系统 (R, +, ×),配有两个二元运算(加法和乘法),满足以下条件:
- (R, +) 是阿贝尔群:加法满足封闭性、结合律、交换律,有加法单位元(记作 0),每个元素有加法逆元(即负元)。
- (R, ×) 是半群:乘法满足封闭性和结合律。
- 分配律:乘法对加法满足左右分配律,即 a × (b + c) = a×b + a×c,(b + c) × a = b×a + c×a。
直觉:环是"可以做加减乘"的代数系统。你有完整的加法结构(包括减法),乘法也有但可能比较"弱"——不一定有乘法单位元,更不一定有乘法逆元。
具体例子:
- (Z, +, ×) 是环。整数可以加减乘,但不能除(1/2 不是整数)。
- (M_n®, +, ×) 是环。n阶实矩阵可以加减乘,但不是所有矩阵都可逆。
- (Z_n, +_n, ×_n) 是环。模n整数环。
环中乘法不一定有单位元。如果环有乘法单位元 1,则称为含幺环(Unital Ring)。
环中乘法不一定满足交换律。如果环的乘法满足交换律,则称为交换环(Commutative Ring)。
域
域(Field):一个代数系统 (F, +, ×),配有加法和乘法两个运算,满足以下条件:
- (F, +) 是阿贝尔群:加法结构完整。
- (F{0}, ×) 是阿贝尔群:去掉零元素后,乘法也构成阿贝尔群——这意味着每个非零元素都有乘法逆元。
- 分配律:乘法对加法满足分配律。
直觉:域是"可以做加减乘除(除数非零)"的代数系统。域比环强得多——在环中你可能不能除,在域中你一定可以除。域是最"自由"的代数系统之一。
环和域的核心区别:
| 环 | 域 | |
|---|---|---|
| 加法 | 阿贝尔群 | 阿贝尔群 |
| 乘法 | 只需要半群(封闭+结合律) | 去掉0后是阿贝尔群 |
| 乘法逆元 | 不一定有 | 每个非零元素都有 |
| 能做除法吗 | 不一定能 | 一定能(除数非零) |
记忆口诀:环可以加减乘,域可以加减乘除。
域的具体例子:
- (Q, +, ×) 有理数域。任意两个有理数可以加减乘除(除数非零)。
- (R, +, ×) 实数域。
- (C, +, ×) 复数域。
- (GF(p), +_p, ×_p) 其中 p 是素数。这是有限域(也叫伽罗瓦域 Galois Field)。为什么 p 必须是素数?因为只有当 p 是素数时,{1, 2, …, p−1} 中每个元素在模 p 乘法下都有逆元。
不是域的例子:
- (Z, +, ×) 不是域。整数环中 2 没有乘法逆元(1/2 不是整数),所以不能做除法。
- (Z_6, +_6, ×_6) 不是域。因为 2 ×_6 3 = 0,存在零因子(两个非零元素相乘等于零)。域不允许零因子存在。
格
在讨论格之前,我们需要先理解偏序关系。
偏序关系(Partial Order):集合 S 上的一个二元关系 ≤,如果满足以下三个条件:
- 自反性:a ≤ a。
- 反对称性:如果 a ≤ b 且 b ≤ a,则 a = b。
- 传递性:如果 a ≤ b 且 b ≤ c,则 a ≤ c。
则称 ≤ 是 S 上的偏序关系,(S, ≤) 称为偏序集(Partially Ordered Set,简称 poset)。
直觉:偏序关系就是"某种意义上的大小关系"。但和全序不同,偏序允许有些元素之间"没法比较大小"。比如集合的包含关系:{1} ⊆ {1,2},但 {1} 和 {2} 之间没有包含关系——它们"不可比较"。
格(Lattice):一个偏序集 (L, ≤),如果对 L 中任意两个元素 a 和 b,都存在:
- 最大下界(Greatest Lower Bound,也叫 meet,记作 a ∧ b):小于等于 a 和 b 的最大元素。
- 最小上界(Least Upper Bound,也叫 join,记作 a ∨ b):大于等于 a 和 b 的最小元素。
则称 (L, ≤) 是一个格。
直觉:格就是一个偏序集,其中任意两个元素都能找到"最佳交汇点"——既能找到它们的"最大公约"(meet),也能找到它们的"最小公倍"(join)。就像两个人约见面,总能找到一个"折中方案"。
格的代数定义:也可以直接用两个运算 ∧ 和 ∨ 来定义。一个代数系统 (L, ∧, ∨) 是格,当且仅当 ∧ 和 ∨ 都满足:
- 交换律:a ∧ b = b ∧ a,a ∨ b = b ∨ a。
- 结合律:(a ∧ b) ∧ c = a ∧ (b ∧ c),(a ∨ b) ∨ c = a ∨ (b ∨ c)。
- 吸收律:a ∧ (a ∨ b) = a,a ∨ (a ∧ b) = a。
吸收律(Absorption Law)是格特有的性质,它连接了两个运算。直觉上,吸收律说的是"取了大的再取小的,结果还是原来那个小的"。
格的具体例子:
-
幂集格:(P(S), ⊆)。对任意两个子集 A, B ⊆ S:
- meet(最大下界)= A ∩ B(交集)
- join(最小上界)= A ∪ B(并集)
-
整除格:正整数按整除关系构成的格。对任意两个正整数 a, b:
- meet = gcd(a, b)(最大公约数)
- join = lcm(a, b)(最小公倍数)
例如在 {1, 2, 3, 6} 上:meet(2, 3) = gcd(2,3) = 1,join(2, 3) = lcm(2,3) = 6。
-
布尔格(Boolean Lattice):每个元素都有补元(complement)的格。补元就是"取反"——a 和它的补元 ā 满足 a ∧ ā = 0(最小元),a ∨ ā = 1(最大元)。布尔格对应布尔代数,是数字电路和计算机逻辑的基础。
例题与详解
例题 1:(Z₆, ×₆) 是不是群?
问题:判断代数系统 (Z₆, ×₆) 是否构成群。其中 Z₆ = {0, 1, 2, 3, 4, 5},×₆ 是模6乘法(即 a ×₆ b = (a × b) mod 6)。
分析思路:群有四个条件——封闭性、结合律、单位元、逆元。我们逐一检查,只要有一个不满足,就不是群。
第一步:检查封闭性
我们需要验证:对任意 a, b ∈ Z₆,(a × b) mod 6 ∈ Z₆。
两个 0 到 5 之间的整数相乘,结果是一个非负整数。对 6 取余后,结果一定在 {0, 1, 2, 3, 4, 5} 中。
封闭性 ✓
第二步:检查结合律
模6乘法的结合律继承自整数乘法的结合律。具体来说:
(a ×₆ b) ×₆ c = ((a × b) mod 6) ×₆ c = (((a × b) mod 6) × c) mod 6
a ×₆ (b ×₆ c) = a ×₆ ((b × c) mod 6) = (a × ((b × c) mod 6)) mod 6
这两个表达式都等于 (a × b × c) mod 6。(这是模运算的基本性质。)
结合律 ✓
第三步:检查单位元
我们需要找一个 e ∈ Z₆,使得对任意 a ∈ Z₆,a ×₆ e = e ×₆ a = a。
试 e = 1:a ×₆ 1 = (a × 1) mod 6 = a mod 6 = a。✓
单位元是 1。✓
第四步:检查逆元(关键步骤)
我们需要验证:对每个 a ∈ Z₆,是否存在 a⁻¹ ∈ Z₆,使得 a ×₆ a⁻¹ = 1。
逐一检查每个元素:
a = 0:需要找 x 使得 0 ×₆ x = 1,即 (0 × x) mod 6 = 1,即 0 mod 6 = 1。这是不可能的,因为 0 mod 6 = 0 ≠ 1。
0 没有逆元!
这已经足以说明 (Z₆, ×₆) 不是群了。但我们继续看看其他元素的情况,以加深理解。
a = 1:1 ×₆ 1 = 1。所以 1⁻¹ = 1。✓
a = 2:需要找 x 使得 2x ≡ 1 (mod 6)。
- 2 × 0 = 0
- 2 × 1 = 2
- 2 × 2 = 4
- 2 × 3 = 6 ≡ 0
- 2 × 4 = 8 ≡ 2
- 2 × 5 = 10 ≡ 4
2x mod 6 只能取 {0, 2, 4},永远取不到 1。2 没有逆元。
为什么会这样?因为 gcd(2, 6) = 2 ≠ 1。2 和 6 有公因数,所以 2 的倍数在模6下"跳过"了很多余数。只有当 gcd(a, n) = 1 时,a 在模 n 乘法下才有逆元。
a = 3:需要找 x 使得 3x ≡ 1 (mod 6)。
- 3 × 0 = 0
- 3 × 1 = 3
- 3 × 2 = 6 ≡ 0
- 3 × 3 = 9 ≡ 3
- 3 × 4 = 12 ≡ 0
- 3 × 5 = 15 ≡ 3
3x mod 6 只能取 {0, 3},永远取不到 1。3 没有逆元。 因为 gcd(3, 6) = 3 ≠ 1。
a = 4:需要找 x 使得 4x ≡ 1 (mod 6)。
- 4 × 0 = 0
- 4 × 1 = 4
- 4 × 2 = 8 ≡ 2
- 4 × 3 = 12 ≡ 0
- 4 × 4 = 16 ≡ 4
- 4 × 5 = 20 ≡ 2
4x mod 6 只能取 {0, 2, 4},永远取不到 1。4 没有逆元。 因为 gcd(4, 6) = 2 ≠ 1。
a = 5:需要找 x 使得 5x ≡ 1 (mod 6)。
- 5 × 5 = 25 = 4 × 6 + 1 ≡ 1 (mod 6)
**5⁻¹ = 5。**✓ 因为 gcd(5, 6) = 1。
结论:(Z₆, ×₆) 不是群,因为 0、2、3、4 都没有逆元。
进一步分析:只有与 6 互质的元素(即 gcd(a, 6) = 1 的元素)才有逆元。Z₆ 中与 6 互质的元素是 1 和 5。实际上,({1, 5}, ×₆) 是一个群(只有两个元素的群,同构于 (Z₂, +₂))。
更一般地,(Z_n{0}, ×_n) 构成群当且仅当 n 是素数。这就是为什么有限域 GF(p) 要求 p 是素数——只有素数模下,所有非零元素才有乘法逆元。
例题 2:证明模n映射是同态
问题:证明 f: (Z, +) → (Z_n, +_n),f(a) = a mod n 是同态。
分析思路:要证明 f 是同态,需要证明 f(a + b) = f(a) +_n f(b),即"先加再取模 = 先取模再加"。
证明:
我们要证明:(a + b) mod n = ((a mod n) + (b mod n)) mod n。
第一步:用带余除法表示 a 和 b。
设 a = q₁n + r₁,其中 0 ≤ r₁ < n。则 r₁ = a mod n。
设 b = q₂n + r₂,其中 0 ≤ r₂ < n。则 r₂ = b mod n。
(这一步用的是带余除法:任意整数 a 都可以唯一地写成 a = qn + r 的形式,其中 q 是商,r 是余数,0 ≤ r < n。)
第二步:计算 a + b。
a + b = (q₁n + r₁) + (q₂n + r₂) = (q₁ + q₂)n + (r₁ + r₂)
第三步:计算 (a + b) mod n。
(a + b) mod n = ((q₁ + q₂)n + (r₁ + r₂)) mod n
因为 (q₁ + q₂)n 是 n 的倍数,所以 ((q₁ + q₂)n + (r₁ + r₂)) mod n = (r₁ + r₂) mod n。
(这一步用的是模运算的基本性质:(kn + m) mod n = m mod n。)
第四步:计算 f(a) +_n f(b)。
f(a) +_n f(b) = (a mod n) +_n (b mod n) = r₁ +_n r₂ = (r₁ + r₂) mod n
第五步:比较两边。
(a + b) mod n = (r₁ + r₂) mod n = f(a) +_n f(b)
所以 f(a + b) = f(a) +_n f(b)。证毕。
这个同态的实际意义:你可以先把大数"缩小"(取模),再做加法,结果和先做大数加法再取模一样。这就是为什么计算机可以用固定位数的整数来做模运算——中间过程取模不影响最终结果。
例题 3:识别代数系统的类型
问题:判断以下代数系统分别属于什么类型(半群、幺半群、群、阿贝尔群、环、域)。
(a) (Z, +)
- 封闭性:整数 + 整数 = 整数。✓
- 结合律:✓
- 单位元:0。✓
- 逆元:a 的逆元是 −a。✓
- 交换律:✓
结论:(Z, +) 是阿贝尔群(交换群)。
(b) (正整数集, ×)
- 封闭性:正整数 × 正整数 = 正整数。✓
- 结合律:✓
- 单位元:1。✓
- 逆元:2 的逆元应该是 1/2,但 1/2 不是正整数。✗
结论:(正整数集, ×) 是幺半群(有单位元的半群),但不是群。
© (Z, +, ×)
- (Z, +) 是阿贝尔群。✓
- (Z, ×) 是半群(封闭、结合律)。✓
- 乘法对加法分配律。✓
结论:(Z, +, ×) 是环(整数环),但不是域(2 没有乘法逆元)。
(d) (Q, +, ×)
- (Q, +) 是阿贝尔群。✓
- (Q{0}, ×) 是阿贝尔群。✓
- 分配律。✓
结论:(Q, +, ×) 是域(有理数域)。
例题 4:魔方与群论
问题:为什么魔方的所有操作构成一个群?
分析:魔方的"操作"是指旋转某一面(如顺时针转顶层90度)。我们把这些操作看作群的元素。
-
封闭性:连续做两个操作(先做操作A,再做操作B),结果还是一个操作(某个特定的旋转组合)。所以操作的"复合"是封闭的。
-
结合律:操作的复合满足结合律。先做A再做(B再C),和先做(A再B)再做C,结果一样。
-
单位元:不做任何操作(“什么都不转”)就是单位元。
-
逆元:每个操作都有逆操作。顺时针转90度的逆操作是逆时针转90度。任何旋转组合都可以"倒着做一遍"来撤销。
所以魔方的所有操作构成一个群,称为魔方群。这个群的大小是 4.3 × 10^19,但它的结构是清晰的——这就是为什么数学家可以分析魔方,而不需要穷举所有状态。
本章速查表
| 概念 | 一句话解释 | 关键条件 |
|---|---|---|
| 二元运算 | 拿两个同类型元素进去,出来一个同类型元素 | 函数 f: S×S → S |
| 封闭性 | 运算结果不会跑到集合外面去 | a∘b ∈ S |
| 交换律 | 顺序无所谓 | a∘b = b∘a |
| 结合律 | 先算哪两个无所谓 | (a∘b)∘c = a∘(b∘c) |
| 分配律 | 一个运算可以"穿透"另一个运算 | a∘(b⊕c) = (a∘b)⊕(a∘c) |
| 单位元 | "什么都不做"的元素 | ∃e, a∘e = e∘a = a |
| 逆元 | "撤销操作"的元素 | ∃a⁻¹, a∘a⁻¹ = e |
| 幂等律 | 自己和自己运算还是自己 | a∘a = a |
| 代数系统 | 集合 + 运算 + 规则 | (S, ∘) |
| 同态 | 保持运算结构的映射 | f(a∘b) = f(a)*f(b) |
| 同构 | 结构完全相同的映射(同态 + 双射) | 一一对应 + 保持运算 |
| 子代数 | 对运算封闭的子集 | ∀a,b ∈ T, a∘b ∈ T |
| 半群 | 封闭 + 结合律 | 最基本的代数系统 |
| 幺半群 | 半群 + 单位元 | 有"静止"但不能"后退" |
| 群 | 封闭 + 结合律 + 单位元 + 逆元 | 可运算、可撤销 |
| 阿贝尔群 | 群 + 交换律 | 顺序无所谓的群 |
| 循环群 | 由一个元素生成的群 | ∃g, G = {g^n} |
| 环 | 加法阿贝尔群 + 乘法半群 + 分配律 | 可以加减乘 |
| 域 | 加法阿贝尔群 + 乘法(去掉0)阿贝尔群 + 分配律 | 可以加减乘除 |
| 偏序关系 | 自反、反对称、传递的关系 | ≤ 关系 |
| 格 | 任意两元素都有最大下界和最小上界 | meet ∧ 和 join ∨ |
| 有限域 GF(p) | p 个元素的域(p 为素数) | 模p算术 |
