[编译原理实验]python实现NFA的确定化

实验要求

  1. 从txt文件读入NFA;
  2. 用Graphviz实现自动机的图形化;
  3. 输出NFA和DFA图像;

输入文件

data.txt
第一行为初态集合,第二行为终态集合
其余是状态转换情况(ps. 本实验$代表空ε)

0
10
0 $ 1
1 $ 2
1 $ 4
2 a 3
3 $ 6
4 b 5
5 $ 6
6 $ 7
7 a 8
8 b 9
9 b 10
6 $ 1
0 $ 7

数据结构

  • Move_NFA ,list类型的list,存放NFA状态变化情况.
  • _closure ,字典,存放NFA中每个状态的空闭包集合
  • Res,字典,存放当前状态遇到每个输入字符的后续状态集,如{‘a’:[‘4’,‘1’,‘2’,‘3’,‘7’,‘6’, ‘8’]}
  • State0 ,list类型,存放NFA的所有状态
  • Str0,list类型,存放NFA的字母表
  • StateList,list类型的列表,存放DFA的所有状态集合.
  • Begin0,End0,list类型,Begin0为DFA的初态集合,End0为DFA的终态集

关键代码

1.文件读入处理
with open('data.txt', 'r') as r:
    NFA = [line.rstrip('\n') for line in r.readlines()]
Move_NFA = NFA[2::]
for i in range(len(Move_NFA)):
    # print(Move_NFA[i])
    Move_NFA[i] = Move_NFA[i].split()  # 以空格为分隔符分开
    
2.画NFA图像

需要导入Graphviz库及下载Graphviz.

from graphviz import Digraph
def graph_nfaprint():  # 画NFA的图像
    g = Digraph('G', filename='NFA.gv', format='png')
    for i in range(len(Move_NFA)):
        g.edge(Move_NFA[i][0], Move_NFA[i][2], label=Move_NFA[i][1])
    for i in range(len(Begin)):
        g.node(Begin[i], color='red')  # 开始节点红色
    for j in range(len(End)):
        g.node(End[j], shape='doublecircle')  # 结束节点双层

    g.view()

输出:
在这里插入图片描述

3.找到所有NFA状态的空闭包集合

求出ε-closure(s),ε-closure(s)表示由状态s经由条件ε可以到达的 所有状态的集合

    for i in range(len(State0)):
        res = [State0[i]]
        for j in range(len(Move_NFA)):
            if Move_NFA[j][0] == State0[i] and Move_NFA[j][1] == '$':  # 查找空闭包
                res.extend(Move_NFA[j][2])
        _closure[State0[i]]=list(set(res))
    # print(_closure )
4.求NFA状态的等价状态

输入为单个状态,返回一个List

def Findclosure(AA):   # 输入一个状态,找这个状态的所有等价状态
    A = _closure[AA]   # 查询输入状态的空闭包
    A0 = []
    for ch in A:
        A0 = A0 + _closure[ch]
    A0 = list(set(A0))
    while set(A) != set(A0):   # 循环直到闭包集合不再变化,得到最终的结果
        A = A0
        A0 = []
        for ch in A:
            A0 = A0 + _closure[ch]
        A0 = list(set(A0))

    return A0
5.DFA的状态遇到输入字符的状态转换情况

构造状态转换表,普遍称为称move函数
如本例:

ε-closure(0) = A
ε-closure(move(A,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(A,b)) = ε-closure(5) = {1,2,4,5,6,7} = C
ε-closure(move(B,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(B,b)) = ε-closure({5,9}) = {1,2,4,5,6,7,9} = D
ε-closure(move(C,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(C,b))=ε-closure(5) = {1,2,4,5,6,7} = C
ε-closure(move(D,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(D,b)) = ε-closure(5) = {1,2,4,5,6,7} = C

ab
ABC
BBD
CBC
DBC
def FindNewState(AB):  # 找DFA状态的单步集合
    res = dict()     # 字典,形如{'a': ['4', '1', '2', '3', '7', '6', '8'], 'b': ['5', '4', '1', '2', '7', '6']}
    for ch in Str0:
        RES = []
        for i in range(len(AB)):
            for j in range(len(Move_NFA)):
                if Move_NFA[j][0] == AB[i] and Move_NFA[j][1] == ch:
                    RES.append(Move_NFA[j][2])
        result = []
        for k in RES:
            result = result+Findclosure(k)
        res[ch] = list(set(result))
    return res
6.画DFA图像

DFA的初始状态是NFA的初始状态的等价状态,终止状态是含有NFA终止状态的所有集合

def graph_dfaprint(StateList, Begin0, End0):  # 画DFA图像
    g = Digraph('G', filename='DFA.gv', format='png')
    Begin1 = []
    End1 = []
    for i in range(len(Begin0)):
        Begin1.append(" ".join(sorted(Begin0[i])))
    for j in range(len(End0)):
        End1.append(" ".join(sorted(End0[j])))  # 从小到大排序

    for i in range(len(StateList)):
        Dictt = FindNewState(StateList[i])
        for j in Str0:
            if StateList[i] != [] and Dictt[j] != []:  # 防止重复
                g.edge(" ".join(sorted(StateList[i])), " ".join(sorted(Dictt[j])), label=j)
    for k in range(len(Begin1)):
        g.node(Begin1[k],color='red', shape='circle')  # 初态结点为红色
    for i in range(len(StateList)):
        g.node(" ".join(sorted(StateList[i])), shape='circle')  # 所有节点为圆形
    for j in range(len(End1)):
        g.node(End1[j], shape='doublecircle')  # 终态结点为双层
    g.view()

输出:
在这里插入图片描述

完整python代码

from graphviz import Digraph
import re
# $代表空ε


def graph_nfaprint():  # 画NFA的图像
    g = Digraph('G', filename='NFA.gv', format='png')
    for i in range(len(Move_NFA)):
        g.edge(Move_NFA[i][0], Move_NFA[i][2], label=Move_NFA[i][1])
    for i in range(len(Begin)):
        g.node(Begin[i], color='red')  # 开始节点红色
    for j in range(len(End)):
        g.node(End[j], shape='doublecircle')  # 结束节点双层

    g.view()

def graph_dfaprint(StateList, Begin0, End0):  # 画DFA图像
    g = Digraph('G', filename='DFA.gv', format='png')
    Begin1 = []
    End1 = []
    for i in range(len(Begin0)):
        Begin1.append(" ".join(sorted(Begin0[i])))
    for j in range(len(End0)):
        End1.append(" ".join(sorted(End0[j])))  # 从小到大排序

    for i in range(len(StateList)):
        Dictt = FindNewState(StateList[i])
        for j in Str0:
            if StateList[i] != [] and Dictt[j] != []:
                g.edge(" ".join(sorted(StateList[i])), " ".join(sorted(Dictt[j])), label=j)
    for k in range(len(Begin1)):
        g.node(Begin1[k],color='red', shape='circle')
    for i in range(len(StateList)):
        g.node(" ".join(sorted(StateList[i])), shape='circle')
    for j in range(len(End1)):
        g.node(End1[j], shape='doublecircle')
    g.view()


def Findclosure(AA):   # 输入一个状态,找这个状态的所有等价状态
    A = _closure[AA]   # 查询输入状态的空闭包
    A0 = []
    for ch in A:
        A0 = A0 + _closure[ch]
    A0 = list(set(A0))
    while set(A) != set(A0):   # 循环直到闭包集合不再变化,得到最终的结果
        A = A0
        A0 = []
        for ch in A:
            A0 = A0 + _closure[ch]
        A0 = list(set(A0))

    return A0


def FindNewState(AB):  # 找DFA状态的单步集合
    res = dict()     # 字典,形如{'a': ['4', '1', '2', '3', '7', '6', '8'], 'b': ['5', '4', '1', '2', '7', '6']}
    for ch in Str0:
        RES = []
        for i in range(len(AB)):
            for j in range(len(Move_NFA)):
                if Move_NFA[j][0] == AB[i] and Move_NFA[j][1] == ch:
                    RES.append(Move_NFA[j][2])
        result = []
        for k in RES:
            result = result+Findclosure(k)
        res[ch] = list(set(result))
    return res


# -----------------------
with open('data.txt', 'r') as r:
    NFA = [line.rstrip('\n') for line in r.readlines()]
Move_NFA = NFA[2::]
for i in range(len(Move_NFA)):
    # print(Move_NFA[i])
    Move_NFA[i] = Move_NFA[i].split()  # 以空格为分隔符分开
# print(Move_NFA)
State = []
Str = []
for i in range(len(Move_NFA)):
    State.append(Move_NFA[i][0])
    State.append(Move_NFA[i][2])
    Str.append(Move_NFA[i][1])

State0 = list(set(State))    # 去重
State0.sort(key=State.index)  # 排序
Str0 = list(set(Str))
Str0.sort(key=Str.index)
if '$' in Str0:
    Str0.remove('$')    # 字符列表去掉ε
# print(State0)
_closure = dict()    # 建立一个空字典,表示由每个状态经由条件ε可以到达的 所有状态的集合
End = NFA[1].split()
Begin = NFA[0].split()

if __name__ == '__main__':
    graph_nfaprint()

    for i in range(len(State0)):
        res = [State0[i]]
        for j in range(len(Move_NFA)):
            if Move_NFA[j][0] == State0[i] and Move_NFA[j][1] == '$':  # 查找空闭包
                res.extend(Move_NFA[j][2])
        _closure[State0[i]]=list(set(res))
    # print(_closure )
    A = Findclosure(State0[0])
    # print(A)      # A为初始状态等价集合
    number = 0
    length = 1
    StateList = []
    StateList .append(A)
    while number < length:
        A2 = FindNewState(StateList[number])
        number = number+1
        for ch in Str0:
            temp = 1
            for p in range(length):
                if set(A2[ch]) == set(StateList[p]):
                    temp = 0
                    break
            if temp == 1:
                StateList .append(A2[ch])
                length = length+1


    Begin0 = []
    End0 = []  # 存放DFA开始状态和结束状态
    for i in range(len(Begin)):
        Begin0.append(Findclosure(Begin[i]))
    for j in range(len(End)):
        for k in range(len(StateList)):
            if End[j] in StateList[k]:
                if StateList[k] not in End0:
                    End0.append(StateList[k])

    while [] in StateList:
        StateList.remove([])   # 删除空状态,防止y 0 $的情况
    print('DFA状态表为:', StateList )
    print('DFA开始状态:', Begin0)
    print('DFA终止状态:', End0)
    graph_dfaprint(StateList, Begin0, End0)  # 画出DFA


DFA状态表为: [['0', '4', '1', '7', '2'], ['3', '8', '6', '4', '1', '7', '2'], ['5', '6', '4', '1', '7', '2'], ['9', '5', '6', '4', '1', '7', '2'], ['5', '6', '10', '4', '1', '7', '2']]
DFA开始状态: [['0', '4', '1', '7', '2']]
DFA终止状态: [['5', '6', '10', '4', '1', '7', '2']]

测试样例2

文件读入

data1.txt

x
z
x 0 z
y 0 x
y 0 y
z 0 x
z 0 z
x 1 x
z 1 y

输出

NFA图像
NFA图像
DFA图像
在这里插入图片描述

DFA状态表为: [['x'], ['z'], ['z', 'x'], ['y'], ['y', 'x'], ['z', 'y', 'x']]
DFA开始状态: [['x']]
DFA终止状态: [['z'], ['z', 'x'], ['z', 'y', 'x']]

小结

本实验实现了NFA到DFA的转换,并用Graphviz实现图像化.

  1. 若在转换中出现空状态要记得把空状态从StateList 中删除,不然会出现空节点.
  2. 处理时要注意状态顺序,set可以顺序无关的匹配相等列表
  3. 本实验使用python,导入Graphviz库进行图像生成,学会了Graphviz的基本使用方法.
  4. DFA的初始状态是NFA的初始状态的等价状态,终止状态是含有NFA终止状态的所有集合
  • 12
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值