【程序语言与编译】NFA转DFA(子集构造法)

适合读者:软考中级备考同学

阅读时间:4分钟

内容:子集构造法原理、算法步骤、示例、例题


1. 为什么需要NFA转DFA?

NFA(非确定有限自动机)构造直观,但直接模拟运行效率较低(需要回溯或并行跟踪多个状态)。DFA(确定有限自动机)每个输入符号唯一确定下一状态,模拟运行速度快,更易于硬件实现或编程。

理论 :NFA和DFA的识别能力等价------任何NFA都可以转换为等价的DFA。转换的标准算法称为子集构造法(Subset Construction)。

软考中常考查子集构造法的基本思想或简单实例的转换。


2. 核心概念:ε-闭包

在NFA中,允许不读入任何输入符号(ε)就转移状态。转换前需要先理解ε-闭包

  • ε-闭包 (ε-closure):从某个状态出发,通过0条或多条ε转移所能到达的所有状态的集合。
  • 例如:状态A有ε边到B,B有ε边到C,则 ε-closure({A}) = {A, B, C}。

3. 子集构造法算法步骤

目标:构造一个DFA,使得它接受的字符串集合与原NFA完全相同。

  1. 初始化

    • 计算NFA初始状态的ε-闭包,记为 S0。这个 S0 就是DFA的初始状态。
    • S0 加入未处理队列,并作为DFA的一个状态。
  2. 对每个未处理的DFA状态(即NFA状态子集),处理所有输入符号

    假设当前DFA状态为 T(T 是一个 NFA 状态子集)。对于每个输入符号 a(通常来自字母表 Σ):

    • 计算 move(T, a):从 T 中任一状态出发,读入一个 a 所能到达的所有NFA状态的集合。
    • 计算 ε-closure(move(T, a)),得到一个新的NFA状态子集 U
    • 如果 U 不为空,且尚未在DFA中出现过,则将 U 作为一个新的DFA状态加入队列。
    • 记录DFA中从 TU 在输入 a 下的转移。
  3. 标记终止状态

    如果DFA的某个状态(即NFA状态子集)中包含NFA的至少一个终止状态,那么该DFA状态为终止状态。

  4. 重复直到所有状态都处理完毕。


4. 示例:NFA转DFA

4.1 原NFA描述

  • 状态:A(初始),B,C(终止)。
  • ε转移:无。
  • 转移:
    • A 读 0 → A 和 B
    • A 读 1 → A
    • B 读 1 → C
  • 其他转移无。

这个NFA识别所有以 01 结尾的串。

4.2 转换过程

第1步:初始状态 ε-closure({A}) = {A}(无ε转移)。记为 D_A = {A}。

第2步:处理 D_A = {A}:

  • 输入 0:move({A},0) = {A, B},ε-closure({A,B}) = {A, B} → 新状态 D_{AB} = {A, B}
  • 输入 1:move({A},1) = {A},ε-closure({A}) = {A} → 已存在 D_A

第3步:处理 D_{AB} = {A, B}:

  • 输入 0:move({A,B},0) = {A, B}(A→A,B;B无0),闭包 {A,B} → 已有 D_{AB}
  • 输入 1:move({A,B},1) = {A, C}(A→A;B→C),闭包 {A, C} → 新状态 D_{AC} = {A, C}

第4步:处理 D_{AC} = {A, C}:

  • 输入 0:move({A,C},0) = {A, B}(A→A,B;C无0),闭包 {A,B} → 已有 D_{AB}
  • 输入 1:move({A,C},1) = {A}(A→A;C无1),闭包 {A} → 已有 D_A

第5步:标记终止状态。原NFA的终止状态是C。在DFA状态中,D_{AC} 包含C,因此 D_{AC} 是终止状态。

4.3 得到的DFA

  • 状态:D_A(初始),D_{AB},D_{AC}(终止)
  • 转移:
    • D_A 读0 → D_{AB};读1 → D_A
    • D_{AB} 读0 → D_{AB};读1 → D_{AC}
    • D_{AC} 读0 → D_{AB};读1 → D_A

可重新命名:将 D_A 命为 q0,D_{AB} 命为 q1,D_{AC} 命为 q2。


5. 简化技巧与注意事项

  • ε闭包 是子集构造法的关键,务必先计算所有状态的ε闭包。
  • 如果NFA没有ε转移,那么每个状态子集的ε闭包就是它本身,过程会简单很多。
  • DFA的状态数在最坏情况下可能是指数级(相对于NFA状态数),但实际题目中通常很小。
  • 考试中常考的是简单NFA(2~4个状态)的转换,只需按步骤逐步推导即可。

6. 经典例题

题目1:已知NFA的转移表如下(无ε转移):

状态 输入a 输入b
0 {0,1} {0}
1 {2} {2}
2 {} {}

初始状态0,终止状态2。用子集构造法转换为DFA。

  • 初始闭包 {0} → D0
  • D0 读a:move({0},a)={0,1} → D01
  • D0 读b:move({0},b)={0} → D0
  • D01 = {0,1} 读a:{0,1}→{0,1,2}(0→0,1;1→2)→ D012
  • D01 读b:{0,1}→{0,2} → D02
  • D012 = {0,1,2} 读a:{0,1,2}→{0,1,2} → D012
  • D012 读b:{0,1,2}→{0,2} → D02
  • D02 = {0,2} 读a:{0,2}→{0,1} → D01
  • D02 读b:{0,2}→{0} → D0
  • 终止状态:包含原终止状态2的DFA状态:D012, D02。

答案:DFA有4个状态:D0(初始),D01,D012(终止),D02(终止)。


题目2 (概念):子集构造法的作用是( )。

A. 将DFA转化为NFA

B. 将NFA转化为等价的DFA

C. 最小化DFA

D. 消除ε转移

答案:B


7. 记忆口诀

NFA转DFA,子集构造法。
初态闭包起,每个符号算。
移动取闭包,新集加进来。
含终态即终态,确定化完成。


8. 给备考同学的一句话

子集构造法是软考中有限自动机部分的进阶考点。理解ε-闭包move两个操作,按步骤逐步推导,就能完成转换。考试中一般不会要求处理过于复杂的NFA,掌握教材上的简单例子即可。如果遇到选择题,记住"子集构造法"这个名字及其作用(NFA→DFA)就能得分。


🔔 本专栏日更2篇,点击头像 → 专栏《软考中级高频考点》订阅

#软考中级 #软件设计师 #NFA转DFA #子集构造法 #有限自动机

相关推荐
SmartBoyW2 小时前
深入ECMAScript规范:彻底搞懂JS隐式类型转换与底层ToPrimitive机制
前端·javascript
牧艺2 小时前
Cursor Rules / Skills 分层设计:让 Agent 像「团队新同事」
前端·人工智能·cursor
光影少年2 小时前
react navite 跨端核心原理
前端·react native·react.js
monologues2 小时前
Vue 3 渲染器的核心秘密:从 VNode 创建到快速 Diff 算法
前端
奇奇怪怪的2 小时前
从开发到生产——生成优化、监控、安全与成本
前端
10share2 小时前
100行代码 模拟实现Vue 响应式系统
前端·vue.js
Heo2 小时前
Vite进阶用法详解
前端·javascript·面试
狂炫冰美式3 小时前
人均配了AI, 为什么公司还是没变快? 🤔 本质还是分布式系统问题
前端·后端·架构
乘风gg4 小时前
多 Agent 不是万能的!搞懂这 5 个原则,少走 1 年弯路!
前端·agent·ai编程
猩猩程序员4 小时前
Vercel 推出 Agent 框架 Eve:让 AI Agent 像写 Web 应用一样简单
前端