各类自动机模拟实现

项目地址: https://github.com/HuiyuanYan/automaton_simulation
注:这个github链接必须复制重新在浏览器打开,不能通过CSDN跳转,否则会挂掉。如果github打不开的话可以参考我这篇文章加速:加速github
也可以在CSDN的gitlab打开: https://gitcode.net/m0_56745306/automaton_simulation
(但有可能更新不及时)

如果喜欢请给个关注和STAR吧WWW

一、概述

本项目基于《编译原理》第二版和《自动机理论、语言和计算导论》第三版,以及网络资料,实现包括DFA、NFA在内的多种自动机,使用python语言进行编程,以期加深对自动机的理解。

二、项目结构

├─picture # 存放生成的DFA/NFA图片
├─src #存放源代码
│  ├─automata #自动机实现代码
│  └─container #使用到的其他容器实现代码
└─test #测试文件

三、项目环境

  • Python 3.9.7
  • graphviz version 6.0.1

四、项目运行

进入test文件夹执行test_all.py可运行全部测试样例。

五、实现原理

1.DFA

1.1 DFA的基本实现

DFA(Deterministic Finite Automaton) \text{DFA(Deterministic Finite Automaton)} DFA(Deterministic Finite Automaton)的形式化定义如下:

一个确定型有穷自动机包括:

  1. 一个有穷的状态集合,通常记作 Q Q Q。
  2. 一个有穷的输入符号集合,通常记作 Σ \Sigma Σ。
  3. 一个转移函数,以一个状态和一个输入符号作为变量,返回一个状态。转移函数通常记作 δ \delta δ。
  4. 一个初始状态,是 Q Q Q中状态之一。
  5. 一个终结状态或接受状态的集合 F F F。集合 F F F是 Q Q Q的子集合。

通常用五元组来讨论和表示 D F A DFA DFA:
A = ( Q , Σ , δ , q 0 , F ) 。 A=(Q,\Sigma,\delta,q_0,F)。 A=(Q,Σ,δ,q0​,F)。

src\automata\DFA.py中,定义了DFA类,定义了它的各个组成:

self.__Q = [] # states
self.__alphabet = []
self.__deltas = {}
self.__q0 = '' # start state
self.__finish_states = [] # finish state

可以通过接口函数add_state/add_states, set_alphabet, set_deltas/add_delta, set_q0, set_finish_states对它们分别进行设置。

1.2 DFA运行

DFA是通过接受状态来接受语言的,即定义 DFA A \text{DFA A} DFA A的语言。这个语言记作 L ( A ) L(A) L(A),定义为:
L ( A ) = { w ∣ δ ^ ( q 0 , w ) ∈ F } L(A) = \{w|\hat{\delta}(q_0,w) \in F\} L(A)={w∣δ^(q0​,w)∈F}
也就是说,语言 A A A是让初始状态 q 0 q_0 q0​通向接受状态之一的串 w w w的集合。如果对某个 DFA A \text{DFA A} DFA A来说 L L L是 L ( A ) L(A) L(A),那么就说 L L L是正则语言

DFA \text{DFA} DFA模拟的算法为:

s = s0;
c = nextChar();
while(c! = eof){s = move(s,c);c = nxtChar();
}
if (s in F) return true;
else return false;

算法实现见函数DFA.run(str)。可以通过设置参数verbose = True来打印每一步的过程。

  • Example:
d = DFA()
d.add_states(['q0','q1','q2','q3'])
d.set_alphabet(['0','1'])
d.set_q0('q0')
d.set_finish_states(['q3'])
d.set_deltas({'q0':[('0','q1')],'q1':[('1','q2')],'q2':[('0','q3')],
})
assert d.run('010') == True

1.3 DFA最小化

DFA最小化对于每个DFA,求出在接受相同语言的任意DFA中具有最少状态数的等价DFA,且该最小DFA是唯一的

算法的大致思路为:

1. 排除所有不能从初始状态到达的状态
2. 把剩下的状态划分为块,同一块中的状态都是等价的,并且不同块中的两个状态一定不等价。

排除不可达状态,可以使用深度优先遍历,从初始状态开始,找到所有可达节点,并删除不可达节点及其转移(transition)。

等价块划分采取“填表算法”(见《自动机理论、语言和计算导论》第二版P107),具体步骤为:

1. 新建一张"n*n"的表,其中n为消除不可达状态后的状态数。实际上,由于对称性,只需要使用对角线下半部分的表即可。2.将所有状态按顺序编号0,1,2...,并对应于表的下标。3.标记结束状态与其他状态不等价(即可区分,填对应表项为1)。4.对表进行迭代操作,每次迭代遍历每个表项t[i][j](i<j),若存在一个字母表中的字符c,可以将状态i,j区分,即状态i和状态j在c上转移的下一个状态被标记为不等价,那么标记t[i][j]可区分。5.进行4中的迭代操作,直至表再无更新。6.将表中未被标记的状态分别合并为一个状态(这里用到了并查集),重新设置状态,并根据原来的转移函数设置新转移函数。

代码见src\automata\DFA.pyDFA.minimize()函数。

1.4 从DFA到正则表达式

通过归纳构造的方式从 DFA \text{DFA} DFA构造正则表达式。

将DFA A中的每个状态编号(0,1,2,…,n)用 R i j k R_{ij}^{k} Rijk​作为正则表达式的名字,属于该正则的串 w w w满足: w w w是从 A A A中从状态 i i i到状态 j j j的路径的标记,且这条路径没有经过编号大于 k k k的中间节点。

#mermaid-svg-kmq6JuZDrBJPMaSu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu .error-icon{fill:#552222;}#mermaid-svg-kmq6JuZDrBJPMaSu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kmq6JuZDrBJPMaSu .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-kmq6JuZDrBJPMaSu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kmq6JuZDrBJPMaSu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kmq6JuZDrBJPMaSu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kmq6JuZDrBJPMaSu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kmq6JuZDrBJPMaSu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kmq6JuZDrBJPMaSu .marker.cross{stroke:#333333;}#mermaid-svg-kmq6JuZDrBJPMaSu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kmq6JuZDrBJPMaSu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu .cluster-label text{fill:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu .cluster-label span{color:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu .label text,#mermaid-svg-kmq6JuZDrBJPMaSu span{fill:#333;color:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu .node rect,#mermaid-svg-kmq6JuZDrBJPMaSu .node circle,#mermaid-svg-kmq6JuZDrBJPMaSu .node ellipse,#mermaid-svg-kmq6JuZDrBJPMaSu .node polygon,#mermaid-svg-kmq6JuZDrBJPMaSu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kmq6JuZDrBJPMaSu .node .label{text-align:center;}#mermaid-svg-kmq6JuZDrBJPMaSu .node.clickable{cursor:pointer;}#mermaid-svg-kmq6JuZDrBJPMaSu .arrowheadPath{fill:#333333;}#mermaid-svg-kmq6JuZDrBJPMaSu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kmq6JuZDrBJPMaSu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kmq6JuZDrBJPMaSu .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-kmq6JuZDrBJPMaSu .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-kmq6JuZDrBJPMaSu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kmq6JuZDrBJPMaSu .cluster text{fill:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu .cluster span{color:#333;}#mermaid-svg-kmq6JuZDrBJPMaSu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kmq6JuZDrBJPMaSu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

1
0
0,1
1
2,finish
start

例如,对于上面的DFA, R 12 ( 0 ) = 0 R_{12}^{(0)}=0 R12(0)​=0,即 0 0 0这个串满足能够从状态1到状态2,且中间没有编号大于0的节点(中间不经过任何节点)。

那么最终我们需要求的DFA的正则表达式则为 R s t a r t _ s t a t e , a l l _ e n d _ s t a t e s ( n ) R_{start\_state,all\_end\_states}^{(n)} Rstart_state,all_end_states(n)​, n n n为状态数,表示满足可以从开始状态到所有结束状态,中间可以经过任意节点的正则表达式。

下面为具体步骤:

归纳的基础为,当 k = 0 k=0 k=0时,由于所有状态的编号都大于等于1,所以这时对路径的限制为:路径根本没有中间状态。只有两种路径满足这样的条件:

  1. 从状态 i i i到 j j j的一条弧。
  2. 只包含某个顶点 i i i的长度为0的路径。

当 i ≠ j i\not=j i=j时,只有情形1是可能的。检查该DFA,找到所有满足的输入符号 a i a_i ai​,使得在 a i a_i ai​上存在从 i i i到 j j j的转移。

  • 如果不存在这样的符号, R i j ( 0 ) = ∅ R_{ij}^{(0)}=\empty Rij(0)​=∅。

  • 如果恰好有一个这样的符号 a a a,则 R i j ( 0 ) = a R_{ij}^{(0)}=a Rij(0)​=a。

  • 如果同时有多个符号 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1​,a2​,...,an​满足,则 R i j ( 0 ) = a 1 + a 2 + . . . + a n R_{ij}^{(0)}=a_1+a_2+...+a_n Rij(0)​=a1​+a2​+...+an​。

当 i = j i=j i=j时,合法路径为长度为0的路径(不进行转移)和从 i i i到其自身的环。长度为0的路径正则表达式记为 ϵ \epsilon ϵ,环的正则表达式则根据上述规则确定。最终 R i i ( 0 ) = ϵ + a 1 + a 2 + . . . + a n R_{ii}^{(0)}=\epsilon+a_1+a_2+...+a_n Rii(0)​=ϵ+a1​+a2​+...+an​。

当 k > 0 k>0 k>0时,从 i i i到 j j j的路径不经过比 k k k高的状态,考虑如下两种情形:

  1. 该路径不经过状态 k k k,此时 R i j ( k ) = R i j ( k − 1 ) R_{ij}^{(k)}=R_{ij}^{(k-1)} Rij(k)​=Rij(k−1)​。
#mermaid-svg-TkMBx38Ieh8Ts9qZ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .error-icon{fill:#552222;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .marker.cross{stroke:#333333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .cluster-label text{fill:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .cluster-label span{color:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .label text,#mermaid-svg-TkMBx38Ieh8Ts9qZ span{fill:#333;color:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .node rect,#mermaid-svg-TkMBx38Ieh8Ts9qZ .node circle,#mermaid-svg-TkMBx38Ieh8Ts9qZ .node ellipse,#mermaid-svg-TkMBx38Ieh8Ts9qZ .node polygon,#mermaid-svg-TkMBx38Ieh8Ts9qZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .node .label{text-align:center;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .node.clickable{cursor:pointer;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .arrowheadPath{fill:#333333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .cluster text{fill:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ .cluster span{color:#333;}#mermaid-svg-TkMBx38Ieh8Ts9qZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TkMBx38Ieh8Ts9qZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

states
i
j
k
  1. 该路径至少经过状态 k k k一次。此时可以把路径分为三段: R i k ( k − 1 ) R_{ik}^{(k-1)} Rik(k−1)​、 ( R k k ( k − 1 ) ) ∗ ( 表示在k处自身转移了0到多次 ) (R_{kk}^{(k-1)})^*(\text{表示在k处自身转移了0到多次}) (Rkk(k−1)​)∗(表示在k处自身转移了0到多次)、 R k j ( k − 1 ) R_{kj}^{(k-1)} Rkj(k−1)​。
#mermaid-svg-hyQbpRE5f69uOSUV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hyQbpRE5f69uOSUV .error-icon{fill:#552222;}#mermaid-svg-hyQbpRE5f69uOSUV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hyQbpRE5f69uOSUV .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-hyQbpRE5f69uOSUV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hyQbpRE5f69uOSUV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hyQbpRE5f69uOSUV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hyQbpRE5f69uOSUV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hyQbpRE5f69uOSUV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hyQbpRE5f69uOSUV .marker.cross{stroke:#333333;}#mermaid-svg-hyQbpRE5f69uOSUV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hyQbpRE5f69uOSUV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hyQbpRE5f69uOSUV .cluster-label text{fill:#333;}#mermaid-svg-hyQbpRE5f69uOSUV .cluster-label span{color:#333;}#mermaid-svg-hyQbpRE5f69uOSUV .label text,#mermaid-svg-hyQbpRE5f69uOSUV span{fill:#333;color:#333;}#mermaid-svg-hyQbpRE5f69uOSUV .node rect,#mermaid-svg-hyQbpRE5f69uOSUV .node circle,#mermaid-svg-hyQbpRE5f69uOSUV .node ellipse,#mermaid-svg-hyQbpRE5f69uOSUV .node polygon,#mermaid-svg-hyQbpRE5f69uOSUV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hyQbpRE5f69uOSUV .node .label{text-align:center;}#mermaid-svg-hyQbpRE5f69uOSUV .node.clickable{cursor:pointer;}#mermaid-svg-hyQbpRE5f69uOSUV .arrowheadPath{fill:#333333;}#mermaid-svg-hyQbpRE5f69uOSUV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hyQbpRE5f69uOSUV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hyQbpRE5f69uOSUV .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-hyQbpRE5f69uOSUV .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-hyQbpRE5f69uOSUV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hyQbpRE5f69uOSUV .cluster text{fill:#333;}#mermaid-svg-hyQbpRE5f69uOSUV .cluster span{color:#333;}#mermaid-svg-hyQbpRE5f69uOSUV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hyQbpRE5f69uOSUV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

states
loop
states
i
j
k

故而有 R i j ( k − 1 ) = R i j ( k − 1 ) + R i k ( k − 1 ) ( R k k ( k − 1 ) ) ∗ R k j ( k − 1 ) R_{ij}^{(k-1)}=R_{ij}^{(k-1)}+R_{ik}^{(k-1)}(R_{kk}^{(k-1)})^*R_{kj}^{(k-1)} Rij(k−1)​=Rij(k−1)​+Rik(k−1)​(Rkk(k−1)​)∗Rkj(k−1)​。

最终对于所有 i i i和 j j j,都可以得到 R i j ( n ) R_{ij}^{(n)} Rij(n)​。假设状态1为初始状态,接受状态集合为 { j 0 , j 1 , . . . j n } {\{j_0,j_1,...j_n\}} {j0​,j1​,...jn​},所有表达式之和 ∑ R 1 j i ( n ) \sum R_{1j_i}^{(n)} ∑R1ji​(n)​即为自动机的语言。

详细代码见src\automata\DFA.py\DFA.to_regex(re)

例子:

d = DFA_SRC.DFA()
d.set_alphabet(['0','1'])
d.add_states(['q0','q1'])
d.set_q0('q0')
d.set_finish_states(['q1'])
d.set_deltas({'q0':[('0','q1'),('1','q0')],'q1':[('0','q1'),('1','q1')]}
)
print(d.to_regex())

对于该DFA:

#mermaid-svg-8TmAijgPBTT9EQnQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ .error-icon{fill:#552222;}#mermaid-svg-8TmAijgPBTT9EQnQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8TmAijgPBTT9EQnQ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-8TmAijgPBTT9EQnQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8TmAijgPBTT9EQnQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8TmAijgPBTT9EQnQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8TmAijgPBTT9EQnQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8TmAijgPBTT9EQnQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8TmAijgPBTT9EQnQ .marker.cross{stroke:#333333;}#mermaid-svg-8TmAijgPBTT9EQnQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8TmAijgPBTT9EQnQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ .cluster-label text{fill:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ .cluster-label span{color:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ .label text,#mermaid-svg-8TmAijgPBTT9EQnQ span{fill:#333;color:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ .node rect,#mermaid-svg-8TmAijgPBTT9EQnQ .node circle,#mermaid-svg-8TmAijgPBTT9EQnQ .node ellipse,#mermaid-svg-8TmAijgPBTT9EQnQ .node polygon,#mermaid-svg-8TmAijgPBTT9EQnQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8TmAijgPBTT9EQnQ .node .label{text-align:center;}#mermaid-svg-8TmAijgPBTT9EQnQ .node.clickable{cursor:pointer;}#mermaid-svg-8TmAijgPBTT9EQnQ .arrowheadPath{fill:#333333;}#mermaid-svg-8TmAijgPBTT9EQnQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8TmAijgPBTT9EQnQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8TmAijgPBTT9EQnQ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-8TmAijgPBTT9EQnQ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-8TmAijgPBTT9EQnQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8TmAijgPBTT9EQnQ .cluster text{fill:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ .cluster span{color:#333;}#mermaid-svg-8TmAijgPBTT9EQnQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8TmAijgPBTT9EQnQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

1
0
0,1
q0
q1,finish
start

输出的结果为
0 + ( ε + 1 ) ( ε + 1 ) ∗ 0 + ( 0 + ( ε + 1 ) ( ε + 1 ) ∗ 0 ) ( ε + 0 + 1 ) ∗ ( ε + 0 + 1 ) 0+(ε+1)(ε+1)^*0+(0+(ε+1)(ε+1)^*0)(ε+0+1)^*(ε+0+1) 0+(ε+1)(ε+1)∗0+(0+(ε+1)(ε+1)∗0)(ε+0+1)∗(ε+0+1)

化简后为 10 ( 0 + 1 ) ∗ 10(0+1)^* 10(0+1)∗。

本功能并未实现对得到正则表达式的化简,需要自己手动进行化简。

2.NFA

2.1 NFA的基本实现

NFA(NonDeterministic Finite Automata) \text{NFA(NonDeterministic Finite Automata)} NFA(NonDeterministic Finite Automata)的形式化定义如下:

一个 NFA A \text{NFA A} NFA A可形式化表述为:

A = ( Q , Σ , δ , q 0 , F ) A=(Q,\Sigma,\delta,q_0,F) A=(Q,Σ,δ,q0​,F)

其中:

  1. Q Q Q是一个有穷的状态集合。
  2. Σ \Sigma Σ是一个有穷的输入符号集合。
  3. q 0 q_0 q0​是初始状态,属于 Q Q Q。
  4. F F F是终结(或接受)状态的集合,是Q的子集合。
  5. 转移函数 δ \delta δ是一个以 Q Q Q中状态和 Σ \Sigma Σ中的一个输入符号作为变量,并返回 Q Q Q的一个子集合的函数。

同样地,在NFA.py中也保留了与DFA.py中类似的成员和接口函数。

需要注意的是,NFA.set_deltas中的参数类型与DFA中有所不同。

2.2 NFA运行

NFA的模拟算法为:

S = ε-closure(s0);
c = nextChar();
while (!c = eof){S = ε-closure(move(S,c));c = nextChar();
}
if(S ∩ F != Empty) return true;
else return false;

算法实现见函数NFA.run(str)。可以通过设置参数verbose = True来打印每一步的过程

2.3 从NFA到DFA

从NFA到DFA的转换,使用子集构造法来实现。算法的描述为:

Dstates = [];
Dtran = {};
Dstates.append([ε-closure(s0),unmarked]);
while there is an unmarked state T in Dstates:Mark T;for every ch in alphabet:U = ε-closure( move(T,ch) );if U not in Dstates:U.append([Dstates,unmarked]);Dtran[T,ch] = U;

然后根据DstatesDtrans重新设计DFA即可。

算法实现见函数NFA.to_DFA()

2.2 从正则表达式到NFA

本项目还实现了从基本正则表达式(包含的运算符号有 “ | ”(或)、“ * ”(闭包)、concat(连接))到NFA的转换。

采用的算法是汤普森构造法。

具体步骤为:

1. 将输入的正则表达式转换为后缀表达式,并添加相应的连接符。(见代码NFA.regex_to_NFA().infix_to_postfix(regex))。2. 根据获取的后缀表达式,利用汤普森构造法构造新NFA。(包含了对各个状态重新编号的过程)

实现见代码NFA.regex_to_NFA()

3.自动机的图形表示

采用pygraphviz包利用grpahviz软件画图。

使用自动机下的draw方法进行调用画图。

默认存储路径为picture文件夹,可以在config.py中进行修改。

六、参考

https://deniskyashif.com/2019/02/17/implementing-a-regular-expression-engine/

NFA、DFA模拟、正则表达式转NFA、NFA转DFA、DFA转正则、DFA最小化的python实现项目相关推荐

  1. 编译原理: 最小化 DFA(划分) 验证 DFA(Kleene 闭包)

    编译原理: 最小化 DFA(划分) & 验证 DFA(Kleene 闭包) 文章目录 编译原理: 最小化 DFA(划分) & 验证 DFA(Kleene 闭包) 简介 参考 正文 示例 ...

  2. 正则表达式转NFA,DFA,最小化DFA

    Exp 2:正则表达式转NFA,DFA,最小化DFA (1)正则表达式应该支持单个字符,运算符号有: 连接 选择(|) 闭包(*) 正闭包(+) 可选(?) 括号 (2)要提供一个源程序编辑界面,让用 ...

  3. 确定有限自动机(DFA)和不确定有限自动机(NFA)的主要区别

    区别点 DFA NFA 转移 对输入的转移是确定的,每个状态对每个输入只有一条路径 对输入的转移是不确定的,每个状态对每个输入可以有多条路径 转移函数 所有转移函数都是部分函数,每个状态都有明确定义的 ...

  4. 有穷自动机 DFA(确定)和NFA(不确定)

    有穷自动机分为DFA和NFA两种.分别位确定的和不确定的两种形式. DFA部分: 一个确定的有穷自动机是一个五元组  M=(K,Σ,f,S,Z)各自的意义分别为: K: 状态集合,有限集合 Σ: 有穷 ...

  5. 编译原理---NFA转化为DFA---DFA最小化(自己看)

    算法描述: 伪代码: function E(s){return NFA中状态s经过空转移到达的状态集合: }function Ee(T){return NFA中状态集合T中的每个状态通过空转移到达的状 ...

  6. 细讲如何对NFA确定化和最小化

    文章目录 NFA的确定化和最小化

  7. 实现一个 DFA 正则表达式引擎 - 4. DFA 的最小化

    (正则引擎已完成,Github) 最小化 DFA 是引擎中另外一个略繁琐的点(第一个是构建语法树). 基本思路是,先对 DFA 进行重命名,然后引入一个拒绝态 0,定义所有状态经过非接受字符转到状态 ...

  8. dfa转正则表达式_从0到1打造正则表达式执行引擎(二)

    本文原文地址https://blog.csdn.net/xindoo/article/details/106458165 在上篇博客从0到1打造正则表达式执行引擎(一)中我们已经构建了一个可用的正则表 ...

  9. dfa转正则表达式_GitHub - hidva/re2dot: 根据正则表达式生成其对应 DFA 的状态转移图...

    re2dot 根据正则表达式生成其对应 DFA 的状态转移图, 简单来说就是输入一个正则表达式, 然后会构造其对应的 NFA, 然后再构造对应的 DFA, 然后再最小化 DFA, 最后使用 dot 语 ...

最新文章

  1. pytorch遇见RuntimeError: CUDA out of memory的解决
  2. 修复Debian grub
  3. 几行代码,网盘链接提头来见!
  4. Matlab中求数据概率分布的方法
  5. c mysql ssh_c ssh mysql数据库
  6. 【Java】列表、集合、哈希表和可变参数
  7. 软件系统中的颗粒度_意式浓缩咖啡丨甘醇香浓余韵长,研磨的度与质千万别忽视...
  8. 46 - 算法 -Leetcode-189-旋转数组-数据结构vector或者reverse函数
  9. 不使用自动注解方式来生成mapper,采用原生方式来生成mapper
  10. C# 将链表存入二进制文件及读取二进制文件得到链表示例
  11. 苏州netapp存储服务器维修,NetApp存储日常维护手册 v12.doc
  12. 谷歌如何在设计上脱胎换骨
  13. c# SIM 卡拨号
  14. android+3.1.2+imagebutton监听,button以及Imagebutton的使用
  15. bootdo框架介绍使用
  16. libCef退出流程整理
  17. oracle数据库02195,数据库表空间操作 - osc_w33tzsln的个人空间 - OSCHINA - 中文开源技术交流社区...
  18. 微端要用什么样的服务器
  19. 悟道-看山是山,看水是水
  20. Python 外星人入侵(一):武装飞船

热门文章

  1. java 获取scheduler_Spring Scheduler定时任务 + Quartz
  2. ARP攻击与防护--小试牛刀
  3. 3D hand pose:Minimal Hand
  4. Ample Sound Ample Bass Metal Ray5 for mac(低弦音软件)
  5. 单纯形法只有两个约束条件_线性规划之单纯形法
  6. 众昂矿业:氟化工行业快速发展,但原材料供需紧张
  7. dw建立站点连接mysql_DW中站点的使用
  8. 遭遇拙劣的skype骗局
  9. mysql etl过程_ETL处理过程
  10. 光电二极管运放电路的线性和如何减小偏置