【形式语言】python实现 ε-ΝFA -> DFA

参考资料
自动机从ε NFA到DFA的转换

Step1
构建ε闭包

输入


# ε-ΝFA -> DFA
import re  # 引入正则表达式模块

# 打开文件
with open(r'.\ΝFA1.txt', encoding='utf-8') as file:  # 第一例
# with open(r'.\ΝFA2.txt', encoding='utf-8') as file:  # 第二例
    lines = file.readlines()  # 按行读取文件
    # for line in lines:  # 展示文件
    #     print(line, end='')

# 创建全局字典,为后面函数所用
e_closure = {}  # 创建空状态转移字典(ε闭包)
state_closure = {}  # 创建状态转移字典,用于保存原状态转移函数


# 创建每一个状态的ε闭包
def Closure():  # 定义e闭包函数
    p1 = re.compile(r"[{](.*?)[}]", re.S)  # 正则表达式匹配大括号中状态

    # ————————————先单独处理第一行,因为带有起始符#————————————
    linelist0 = lines[1].split()  # 将一行解析为列表形式
    # 求第一行的状态转移函数,并保存
    state_closure[linelist0[0][1:3] + '0'] = linelist0[1][1:-1].split()
    state_closure[linelist0[0][1:3] + '1'] = linelist0[2][1:-1].split()

    trdstate = str(re.findall(p1, linelist0[3]))  # third state 用于保存e转移的所有状态
    if trdstate == "['']":  # 如果e转移为空
        e_closure[linelist0[0][1:3]] = [linelist0[0][1:3]]  # 那么e闭包只包含当前状态
    else:
        string1 = [linelist0[0][1:3]]
        for i in trdstate[2:-2].split(','):
            string1.append(i)
        for j in range(2, len(lines)):  # 遍历当前行下面每一行的e转移
            linelist01 = lines[j].split()
            trdstate = str(re.findall(p1, linelist01[3]))  # third state 用于保存e转移的所有状态
            if trdstate == "['']":  # 如果e转移为空
                break
            string1.append(trdstate[2:-2])
        e_closure[linelist0[0][1:3]] = string1

    # ————————————遍历中间行————————————
    for i in range(2, len(lines) - 1):
        linelist = lines[i].split()  # 将一行解析为列表形式
        # 求状态转移函数
        state_closure[linelist[0] + '0'] = linelist[1][1:-1].split()
        state_closure[linelist[0] + '1'] = linelist[2][1:-1].split()

        trdstate = str(re.findall(p1, linelist[3]))  # third state 用于保存e转移的所有状态
        if trdstate == "['']":  # 如果e转移为空
            e_closure[linelist[0]] = [linelist[0]]  # 那么e闭包只包含当前状态
        else:
            string1 = [linelist[0], trdstate[2:-2]]
            for j in range(i + 1, len(lines)):  # 遍历当前行下面每一行的e转移
                linelist01 = lines[j].split()
                trdstate = str(re.findall(p1, linelist01[3]))  # third state 用于保存e转移的所有状态
                if trdstate == "['']":  # 如果e转移为空
                    break
                string1.append(trdstate[2:-2])
            e_closure[linelist[0]] = string1

    # ————————————单独处理最后一行,因为带有终止符*————————————
    i = len(lines) - 1
    linelisti = lines[i].split()  # 将一行解析为列表形式
    # 求状态转移函数
    state_closure[linelisti[0][1:3] + '0'] = linelisti[1][1:-1].split()
    state_closure[linelisti[0][1:3] + '1'] = linelisti[2][1:-1].split()

    trdstate = str(re.findall(p1, linelisti[3]))  # third state 用于保存e转移的所有状态
    if trdstate == "['']":  # 如果e转移为空
        e_closure[linelisti[0][1:3]] = [linelisti[0][1:3]]  # 那么e闭包只包含当前状态
    else:
        e_closure[linelisti[0][1:3]] = [linelisti[0][1:3], trdstate[2:-2]]

    return e_closure, state_closure  # 返回元组,0号元素为e闭包,1号元素为状态转移字典


# 构造新状态列表
def New_state_closure():
    q = []  # 创建新状态列表
    # ————————————处理第一个状态————————————
    q.append(e_closure['q0'])  # 第一个状态设置为q0的ε闭包

    l0 = []  # 创建input为0时的状态列表
    l1 = []  # 创建input为1时的状态列表
    for state in e_closure['q0']:  # 遍历第一个新状态的子状态
        for i in state_closure.keys():  # 遍历子状态转移集
            if i == state + '0' and state_closure[i] != []:  # 如果为当前状态,且input = '0',并排除空集
                l0 += state_closure[i]  # 保存状态
            if i == state + '1' and state_closure[i] != []:
                l1 += state_closure[i]

    # 将新状态分情况保存在新状态列表
    if l0 == [] and l1 != []:
        q.append([])
        q.append(e_closure[l1[0]])
    elif l0 != [] and l1 == []:
        q.append(e_closure[l0[0]])
        q.append([])
    elif l0 == [] and l1 == []:
        q.append([])
        q.append([])
    else:
        q.append(e_closure[l0[0]])  # 保存的为其ε闭包
        q.append(e_closure[l1[0]])

    # ————————————处理其余状态————————————
    n = 1  # 从状态列表q中第一个元素开始遍历
    chongfu = 0  # 重复判定初始为0(不重复)
    while True:
        l0 = []  # 创建input为0时的状态列表
        l1 = []  # 创建input为1时的状态列表
        if n != 1:
            for i in range(1, n):  # 判断前面是否有该状态
                if q[n] == q[i]:
                    chongfu = 1  # 重复
                    break
                else:
                    chongfu = 0  # 不重复
        if q[n] == [] or chongfu:  # 如果该状态为空集或重复
            if len(q) == n + 1:  # 如果列表结束
                return q  # ~~~~~~~~~~~~函数结束,返回新状态列表~~~~~~~~~~~~
            else:  # 则跳到下一状态
                n += 1

        else:  # 继续更新新状态列表
            for state in q[n]:  # 遍历第n个新状态的子状态
                for i in state_closure.keys():  # 遍历子状态转移集
                    if i == state + '0' and state_closure[i] != []:  # 如果为当前状态,且input = '0',并排除空集
                        l0 += state_closure[i]  # 保存状态
                    if i == state + '1' and state_closure[i] != []:
                        l1 += state_closure[i]

            if l0 == [] and l1 != []:
                q.append([])
                q.append(e_closure[l1[0]])
            elif l0 != [] and l1 == []:
                q.append(e_closure[l0[0]])
                q.append([])
            elif l0 == [] and l1 == []:
                q.append([])
                q.append([])
            else:
                q.append(e_closure[l0[0]])
                q.append(e_closure[l1[0]])
            n += 1

# 格式化输出新状态列表
def Print_New_state(Q):
    # 重命名
    record = []  # 记录列表,用于保存已经重新命名的元素
    n = 1
    i = 1
    while True:
        if (f'q{n}' not in record) and (Q[i] not in record):
            now = Q[i]
            if now == []:
                Q[i] = ''
                i += 1
                if i == len(Q):
                    break
            else:
                for j in range(i, len(Q)):
                    if Q[j] == now:
                        Q[j] = f'q{n}'
                record.append(f'q{n}')
                n += 1
                i += 1
        else:
            i += 1
            if i == len(Q):
                break
    for i in range(1, len(Q)):  # 加括号
        Q[i] = '{' + Q[i] + '}'

    # 按格式输出
    print('0 1 epsilon')
    for i in range(1, len(Q), 2):
        if i == 1:
            print('#q0 %s %s' % (Q[i], Q[i + 1]))
        elif i == len(Q) - 2:
            print('*q%s %s %s' % (i - 3, Q[i], Q[i + 1]))
        else:
            print('q%s %s %s' % (i - 2, Q[i], Q[i + 1]))


if __name__ == '__main__':
    closures = Closure()
    # print(Closure())  # 展示e闭包和状态转移字典
    Q = New_state_closure()
    # print(Q)  # 展示新状态列表Q
    Print_New_state(Q)

经验总结:

先把所有要点都明确之后再搞,要不全是bug
先用伪代码在pycharm里写一遍
可以用思维导图解释算法流程

赞赏