
NFA到DFA子集构造法 (Subset Construction)
🎯 大阪大学情報工学・計算理论考前冲刺必胜笔记
在大阪大学计算理论近年的出题规律中,NFA 到 DFA 的子集构造法 (Subset Construction) 几乎是必考技能(如 2012、2016、2026 年真题)。本笔记旨在帮助你理清所有数学符号、纠正手写常犯错误、补充知识盲区,并实现“在考场上 5 分钟内无错完成转换”的目标。
📌 目录
-
认知重构:为什么需要子集构造法?
-
数学符号与直观理解(扫除认知死角)
-
核心计算规则:状态转移与接受状态判断
-
高级拓展:处理 ϵ-transition(补充盲区例题)
-
考试通用算法:BFS 状态展开表法(防漏技巧)
-
双重实战演练(含纠错与阪大 2026 真题标准答案)
1. 认知重构:为什么需要子集构造法?
在自动机理论中,DFA(确定有限自动机)和 NFA(非确定有限自动机)在表达能力(Language Recognition)上是完全等价的。但是它们的运行机制有着本质的区别:
【NFA 的不确定性示例】
0
q0 -------> q1
\ 0
\-------> q2
当 NFA 读入 0 时,可能在 q1,也可能在 q2。在物理现实中,我们无法让机器“同时存在于两个平行宇宙”。 因此,我们换一个视角来看 DFA:
DFA 的当前状态,本质上就是“当前 NFA 机器可能所在的所有状态的集合”。
这就是 Subset (子集) 构造法 的由来。我们要构造一个全新的 DFA,它的每一个“状态”都是原 NFA 状态集合的一个“子集”。
2. 数学符号与直观理解(扫除认知死角)
在正式做题前,必须彻底搞懂符号的数学定义,这是写出完美卷面步骤的基础。
2.1 什么是 δ (Delta)?
在自动机中,δ 表示 状态转移函数 (Transition Function)。
-
对于 DFA:δ(q1,a)=q2
- 人话:当前在状态 q1,读入字符 a,去向唯一的状态 q2。
-
对于 NFA:δ(q1,a)={q2,q3}
-
人话:当前在状态 q1,读入字符 a,可能去向的集合是 {q2,q3}。
-
数学形式统一:即使只去一个状态,也必须写成集合形式,如 δ(q0,0)={q1}。
2.2 状态 q1 与 状态集合 {q1} 的本质区别
-
q1:原始 NFA 中的一个单个物理节点(元素)。
-
{q1}:子集构造法中,由我们人为创建的 DFA 的一个新节点(单元素集合)。
-
{q0,q2}:DFA 中的一个新节点。在 DFA 中,它是一个整体,代表“此时 NFA 机器可能在 q0 或 q2”,千万不要把它拆开看作两个步骤。
3. 核心计算规则:状态转移与接受状态判断
3.1 集合的转移计算
如果新 DFA 当前处于状态 R(R 是 NFA 状态的集合),读入符号 a,我们要如何计算它会转移到哪个新状态?
δD(R,a)=⋃q∈RδN(q,a)
🔑 考试口诀:分别询问集合中的每个成员去哪里,然后把所有的答案取并集 (Union),最后去重。
💡 极简计算练习
已知某 NFA 满足:
-
δN(q0,0)={q0,q1}
-
δN(q1,0)=∅
求:δD({q0,q1},0)? 【解析】
δD({q0,q1},0)=δN(q0,0)∪δN(q1,0)={q0,q1}∪∅={q0,q1}
3.2 Accept State (接受状态) 的判定
这是阪大考试中最容易失分的技术细节。
FD={R⊆Q∣R∩FN=∅}
💡 示例直观判定
假设原 NFA 的接受状态集 FN={q2}。判定以下 DFA 状态是否为接受状态:
-
{q0,q1} → ❌ Reject (不含 q2)
-
{q2} → Accept (包含 q2)
-
{q0,q2} → Accept (包含 q2)
-
{q0,q1,q2} → Accept (包含 q2)
4. 高级拓展:处理 ϵ-transition(补充盲区例题)
若 NFA 中存在 ϵ-transition(即空移,无需读入字符即可转移),则必须在构造时计算 ϵ-闭包 (ϵ-closure)。
4.1 什么是 ϵ-closure?
- 定义:ϵ-closure(q) 是指从状态 q 出发,仅通过 ϵ 转移(0 条或多条)所能到达的所有状态的集合(自身默认包含在内)。
4.2 计算公式修正
-
DFA 初始状态:不再是单独的 {q0},而是:
SD=ϵ-closure(q0)
-
转移后做闭包:在对每个集合成员求符号 a 的转移后,必须对结果集合里的每一个状态再求一次 ϵ-closure:
δD(R,a)=ϵ-closure(⋃q∈RδN(q,a))
📝 补充经典例题:含 ϵ 转移的 NFA 到 DFA 转换
【题目】 给定如下含 ϵ 转移的 NFA:
-
状态集:{q0,q1,q2},初始状态 q0,接受状态集 F={q2}。
-
转移关系:
-
δ(q0,a)={q0}, δ(q0,ϵ)={q1}
-
δ(q1,b)={q2}
-
δ(q2,a)={q2}
【求解步骤】
第一步:计算所有状态的 ϵ-closure
-
ϵ-closure(q0)={q0,q1} (通过 ϵ 可以由 q0 走到 q1)
-
ϵ-closure(q1)={q1}
-
ϵ-closure(q2)={q2}
第二步:求 DFA 的初始状态
- SD=ϵ-closure(q0)={q0,q1}
第三步:迭代展开状态转移表
-
对于状态 {q0,q1}:
-
输入 a:
δN(q0,a)∪δN(q1,a)={q0}∪∅={q0}
求闭包:ϵ-closure({q0})={q0,q1}。
-
输入 b:
δN(q0,b)∪δN(q1,b)=∅∪{q2}={q2}
求闭包:ϵ-closure({q2})={q2}。
-
对于新状态 {q2}:
-
输入 a:
δN(q2,a)={q2}
求闭包:ϵ-closure({q2})={q2}。
-
输入 b:
δN(q2,b)=∅→∅
【所得 DFA 转移表】
| DFA 状态 | 输入 a | 输入 b | 是否 Accept |
|---|
| {q0,q1} (Start) | {q0,q1} | {q2} | ❌ Reject |
| {q2} (Accept) | {q2} | ∅ | Accept (含 q2) |
5. 考试通用算法:BFS 状态展开表法(防漏技巧)
为了避免在紧张的考场中漏算、重复计算或写错状态,建议采用 队列 + 发现状态表 的 BFS 机械化计算流程。
【BFS 计算流程图】
确定初始状态 S0 (求闭包)
│
▼
放入队列 Q 并记入“已发现状态表”
│
┌────►│ (队列 Q 非空?)
│ ▼
│ 出队一个状态 R
│ │
│ ▼
│ 分别计算在每个输入字符 a 处的转移:R_new = δ_D(R, a)
│ │
│ ├─► 若 R_new 是一个全新状态:加入队列 Q,记入“已发现状态表”
│ └─► 若 R_new 是已存在状态:不作处理
│ │
└─────┴───────────────────────────────────── 当队列为空时,算法停止
6. 双重实战演练(含纠错与标准答案)
📝 实战一:常规自测题(深刻纠错)
【题目描述】 给定 NFA(Alphabet Σ={0,1},Start = q0,Accept = {q2},无 ϵ 转移):
| NFA 状态 | 输入 0 | 输入 1 |
|---|
| q0 | {q0,q1} | {q0} |
| q1 | {q2} | {q2} |
| q2 | ∅ | {q2} |
🚨 典型错误剖析(手写笔记纠错)
在新接触子集构造法时,容易直接把 NFA 的状态重新组合成 DFA。例如,手写笔记曾将 DFA 的状态写成了 {q0},{q1},{q2}。
-
为什么错?
- DFA 的状态本身就是集合(例如 {q0,q1})。在转移过程中,这些集合作为一个整体的节点存在。不能直接拆成 q0,q1,q2 各自独立的转移。
✍️ 完美推导过程
-
初始状态:A={q0}
-
处理 A={q0}:
-
δD({q0},0)={q0,q1} (发现新状态 B={q0,q1})
-
δD({q0},1)={q0} (指向 A)
-
处理 B={q0,q1}:
-
δD({q0,q1},0)=δN(q0,0)∪δN(q1,0)={q0,q1}∪{q2}={q0,q1,q2} (发现新状态 C={q0,q1,q2})
-
δD({q0,q1},1)=δN(q0,1)∪δN(q1,1)={q0}∪{q2}={q0,q2} (发现新状态 D={q0,q2})
-
处理 C={q0,q1,q2}:
-
δD({q0,q1,q2},0)={q0,q1}∪{q2}∪∅={q0,q1,q2} (指向 C)
-
δD({q0,q1,q2},1)={q0}∪{q2}∪{q2}={q0,q2} (指向 D)
-
处理 D={q0,q2}:
-
δD({q0,q2},0)={q0,q1}∪∅={q0,q1} (指向 B)
-
δD({q0,q2},1)={q0}∪{q2}={q0,q2} (指向 D)
📊 最终答案(DFA 转移表)
| DFA 状态 | 输入 0 | 输入 1 | 是否 Accept |
|---|
| {q0} | {q0,q1} | {q0} | ❌ |
| {q0,q1} | {q0,q1,q2} | {q0,q2} | ❌ |
| {q0,q2} | {q0,q1} | {q0,q2} | (含 q2) |
| {q0,q1,q2} | {q0,q1,q2} | {q0,q2} | (含 q2) |
📝 实战二:2026年大阪大学计算理论真题(终极演练)
【真题要求】 已知 NFA N2 如下图所示:
-
状态集:{s0,s1,s2},初始状态 s0,接受状态集 F={s2}。
-
转移关系同上。
-
任务:构造与之等价、且状态数最少的 DFA,并写出状态对应关系。
【原 NFA 结构图】
┌─── 0 ───┐
▼ │
──► (s0) ──0──► (s1) ──0,1──► ((s2)) ──1──┐
│ ▲ ▲ │
│ └─── 1 ───────────────────┼────────┘
└────────────── 1 ──────────┘
🚨 手写草稿致命错误纠正
你在草稿中写出:
DFA 一共有 3 个状态:{s0}, {s0,s1}, {s0,s1,s2}。
✍️ 完美答题推导步骤
第 1 步:利用子集构造法展开转移
| 序号 | 标识 | DFA 状态 | 输入 0 | 输入 1 | 是否为 Accept State |
|---|
| 1 | A | {s0} | {s0,s1} | {s0} | ❌ Reject |
| 2 | B | {s0,s1} | {s0,s1,s2} | {s0,s2} | ❌ Reject |
| 3 | C | {s0,s2} | {s0,s1} | {s0,s2} | Accept (含 s2) |
| 4 | D | {s0,s1,s2} | {s0,s1,s2} | {s0,s2} | Accept (含 s2) |
第 2 步:状态数最少化 (Minimization) 的逻辑证明 考题中明确要求“状态数最少”。我们需要验证以上 4 个状态是否可以进一步合并。
我们将 DFA 状态划分为非接受状态集 P1={A,B} 与接受状态集 P2={C,D}。
-
测试 P1={A,B} 是否能合并:
-
对于状态 A={s0}:
- 输入
0 转移到 {s0,s1}=B∈P1
-
对于状态 B={s0,s1}:
- 输入
0 转移到 {s0,s1,s2}=D∈P2
-
结论:因为 A 和 B 在输入 0 时转移到了不同的划分集合(一个去 P1,一个去 P2),所以它们不等价,不可合并。
-
测试 P2={C,D} 是否能合并:
-
对于状态 C={s0,s2}:
- 输入
0 转移到 {s0,s1}=B∈P1
-
对于状态 D={s0,s1,s2}:
- 输入
0 转移到 {s0,s1,s2}=D∈P2
-
结论:因为 C 和 D 在输入 0 时转移到了不同的划分集合(一个去 P1,一个去 P2),所以它们也不等价,不可合并。
因此,该由 4 个状态构成的 DFA 已经是最简形式。
第 3 步:给出的最终对应关系(满分答卷样式)
等价 DFA 共有 4 个状态,定义为 {A,B,C,D},状态转移及对应关系如下:
-
状态 A(初始状态):对应原 NFA 状态集合 {s0}。
-
状态 B:对应原 NFA 状态集合 {s0,s1}。
-
状态 C(接受状态):对应原 NFA 状态集合 {s0,s2}。
-
状态 D(接受状态):对应原 NFA 状态集合 {s0,s1,s2}。
【最简等价 DFA 状态图】
0 1
──► (A) ───► (B) ─────────────► ((C)) ◄┐
▲ │ │ │ │ 1
1 │ │ 0 │ └───┘
└── (A) ◄┴─────► ((D)) ────┘
▲ │
└───┘ 0
7. 阪大高分秒杀口诀与防漏技巧
为了保证考试万无一失,请在演练和考场上牢记以下黄金规则:
-
第一步算闭包:一拿到题目,先看有没有 ϵ 转移。如果有,初始状态必须先求闭包。
-
建表防漏:在草稿纸上准备一个“已发现状态表”,算完一个打一个勾。
-
不要脑补未达状态:理论状态数最多有 2n 个,但只列出从初始状态可达 (Reachable) 的集合,多余的状态千万不要画,浪费时间且容易扣分。
-
死状态 (Dead State) 处理:如果题目要求“所有输入都有定义”(全定义 DFA),而某个状态在输入某个字符时没有任何转移(即返回 ∅),则必须显式引入一个死状态 {∅},使其所有转移指向自身。