1. 程式人生 > 其它 >編譯原理:NFA轉DFA(含資料視覺化)

編譯原理:NFA轉DFA(含資料視覺化)


def printlist(l):
    for i in range(len(l)):
        print(l[i], end=" ")


print("----------------------------輸入NFA樣例-----------------------------")
ab = eval(input("請輸入變數的個數"))
print("請輸入變數名稱(用_(下劃線)表示空)")
aabb = []
for i in range(1, ab + 1):
    aabb_eval = input("第" + str(i) + "個變數的名稱是: ")
    aabb.append(aabb_eval)
printlist(aabb)
# [a,b,c]
# 輸入變數的個數之後將為這個創造字典
l = [{} for i in range(ab)]  # 建立和變數相同個數的字典
n = 0
while n < ab:
    print("開始構造變數" + aabb[n] + "的兩節點狀態")
    while True:
        status = input("請輸入起始狀態")
        end_status = input("請輸入終止狀態")
        if status != '#' or end_status != '#':
            l[n].setdefault(status, end_status)
        else:
            print(aabb[n] + "變數的構造輸入已經結束")
            break
    n = n + 1

for i in range(len(l)):
    if aabb[i] == '_':
        print("空變數的節點起始關係為:" + str(l[i]))
    else:
        print("變數" + aabb[i] + "的節點起始關係為:" + str(l[i]))

print(l)
# 空變數的節點起始關係為:{'2': '3', '1': '4', '5': '6'}
# 變數a的節點起始關係為:{'1': '4', '3': '5'}
# 變數b的節點起始關係為:{'5': '2', '3': '5'}

key_list = []
for i in range(len(l)):
    for k in l[i].keys():
        if k not in key_list:
            key_list.append(k)
print("狀態節點有:", end=" ")
# {1,2,3,4,5}
printlist(key_list)
print()
start = input('請輸入起始節點')
end_list = []
a = ""
while a != "#":
    a = input("請輸入終止節點(集),以#表示結束")
    end_list.append(a)
end_list.pop()
print("起始節點是:" + start)
print("終止節點(集)是:", end="")
printlist(end_list)
# 變數a的節點起始關係為:{'1': '2', '3': '4'}
# 變數b的節點起始關係為:{'1': '3', '5': '2'}
# 空變數的節點起始關係為:{'2': '3', '4': '5'}
# 狀態節點有: 1 3 5 2 4
# 請輸入起始節點1
# 請輸入終止節點(集),以#表示結束3
# 請輸入終止節點(集),以#表示結束#
# 起始節點是:1
# 終止節點(集)是:3
print()
print("\n-------------------將狀態節點儲存到檔案中-----------------")
with open('nfa.txt', 'w') as f:
    f.write(start + "\n")
    for i in range(len(end_list)):
        f.write(end_list[i] + " ")
    f.write("\n")
    for j in range(len(l)):  # j表示第幾個字典
        for k in range(len(l[j])):  # k表示第幾個鍵值對
            for m in l[j].keys():  # m表示第幾個鍵
                f.write(m + " " + aabb[j] + " " + l[j][m] + "\n")
print("\n------------------讀取檔案中的資訊----------------------")
with open('nfa.txt', 'r') as r:
    nfa = []
    for line in r.readlines():  # 將檔案資料內容儲存到nfa列表當中
        line_rstrip = line.rstrip('\n')
        nfa.append(line_rstrip)
    # print(nfa)
begin_start = nfa[0].split()  # 起始節點
#print(begin_start)
end_end_list = nfa[1].split()  # 終止節點
#print(end_end_list)
# 如果是按照前面的方法建立的,那麼以上程式碼沒有必要,直接用start和end_list即可
trans_nfa = nfa[2:]
#print(trans_nfa)
for i in range(len(trans_nfa)):
    trans_nfa[i] = trans_nfa[i].split()
#print(trans_nfa)
# #[['1', 'a', '2'], ['3', 'a', '4'], ['1', 'a', '2'], ['3', 'a', '4'], ['1', 'b', '2'], ['3', 'b',
# '4'], ['1', 'b', '2'], ['3', 'b', '4'], ['1', '_', '2']] 所有的狀態已經標識好了
state_to_draw = []  # 狀態節點
para_to_draw = []  # 轉換變數
for i in range(len(trans_nfa)):
    state_to_draw.append(trans_nfa[i][0])
    state_to_draw.append(trans_nfa[i][2])
    para_to_draw.append(trans_nfa[i][1])

# print(state_to_draw)
# print(para_to_draw)

state0 = list(set(state_to_draw))  # 去除節點重複
state0.sort(key=state_to_draw.index)  # 對節點進行排序
para0 = list(set(para_to_draw))
para0.sort(key=para_to_draw.index)
# print(state0)  # ['0', '1', '2', '4', '3', '6', '5', '7', '8', '9', '10']
# print(para0)  # ['_', 'a', 'b']
if '_' in para0:
    para0.remove('_')
# 去掉空

_closure = dict()  # 表示空串可以到達的集合

print("\n--------------開始作圖-------------------")
from graphviz import Digraph


def draw_nfa():
    g = Digraph('G', filename='nfa.gv', format='png')
    for i in range(len(trans_nfa)):
        g.edge(trans_nfa[i][0], trans_nfa[i][2], label=trans_nfa[i][1])
    for i in range(len(begin_start)):
        g.node(begin_start[i], color='red')
    for i in range(len(end_end_list)):
        g.node(end_end_list[i], shape='doublecircle')
    g.view()


draw_nfa()
print("\n------------nfa作圖完畢------------------")
print("\n-----------開始進行NFA轉DFA---------------")

## 尋找所有空閉包
for i in range(len(state0)):
    res = [state0[i]]  # 第i個節點
    # print(res)
    for j in range(len(trans_nfa)):
        if trans_nfa[j][0] == state0[i] and trans_nfa[j][1] == '_':
            # 如果目前研究的節點裡面存在著空
            res.extend(trans_nfa[j][2])
            # 這裡用
    _closure[state0[i]] = list(set(res))
    # 到此,一個狀態所有的空閉包都會顯現出來,接下來只要一查到底即可


# print(_closure)
# {'0': ['1', '0', '7'], '1': ['1', '2', '4'], '2': ['2'], '4': ['4'], '3': ['6', '3'], '6': ['7', '6', '1'], '5': ['5', '6'], '7': ['7'], '8': ['8'], '9': ['9'], '10': ['10']}


# 空閉包的遞迴查詢------
def find_closure(state_input):
    state_now = _closure[state_input]  # 查詢輸入一個狀態下是否有空閉包 ['1', '0', '7']
    state_now_list = []
    for state_now_para in state_now:
        state_now_list = state_now_list + _closure[state_now_para]  # 遞迴查詢
    state_now_list = list(set(state_now_list))
    ## 嘗試輸出
    #print(state_now_list)  # ['0', '7', '1', '2', '4']
    while set(state_now) != set(state_now_list):  # 多次查詢
        state_now = state_now_list
        state_now_list = []
        for state_now_para in state_now:
            state_now_list = state_now_list + _closure[state_now_para]
        state_now_list = list(set(state_now_list))
    return state_now_list


A = find_closure(state0[0])  # 初始狀態的空閉包
#print(A)  # ['1', '7', '4', '2', '0']


def find_state(l_state):
    res = dict()  # 建立一個字典,裡面的值是狀態集合
    for c in para0:  # ['_', 'a', 'b']
        res_two = []
        for i in range(len(l_state)):
            for j in range(
                    len(trans_nfa)):  # [['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']]
                if trans_nfa[j][0] == l_state[i] and trans_nfa[j][1] == c:
                    res_two.append(trans_nfa[j][2])
        result = []
        for k in res_two:
            result = result + find_closure(k)
        res[c] = list(set(result))
    return res


number = 0
length = 1
state_list = []
state_list.append(A)  # [['0', '7', '1', '2', '4']]
while number < length:
    A2 = find_state(state_list[number])
    number = number + 1
    for c in para0:
        temp = 1
        for p in range(length):
            if set(A2[c]) == set(state_list[p]):
                temp = 0
        if temp == 1:
            state_list.append(A2[c])
            length = length + 1
#print(state_list)
# [['0', '2', '4', '1', '7'],
# ['2', '3', '6', '4', '8', '1', '7'],
# ['2', '6', '4', '5', '1', '7'],
# ['9', '2', '6', '4', '5', '1', '7'],
# ['2', '6', '4', '10', '5', '1', '7']]

#獲取開始節點和結束節點
dfa_begin = []
dfa_end = []
for i in range(len(begin_start)):
    # 檢視是否存在空……變數
    dfa_begin.append(find_closure(begin_start[i]))
for j in range(len(end_end_list)):
    for k in range(len(state_list)):
        if end_end_list[j] in state_list[k]:
            #如果nfa的結束節點是在我的某一個狀態列表裡面
            if state_list[k] not in dfa_end:
                dfa_end.append(state_list[k])

print(dfa_begin)
print(dfa_end)
while [] in state_list:
    state_list.remove([])

print('DFA狀態表: ', state_list)
print("DFA起始狀態: ", dfa_begin)
print("DFA終止狀態:", dfa_end)

# DFA狀態表為: [['4', '2', '0', '1', '7'], ['4', '2', '1', '7', '8', '3', '6'], ['4', '2', '1', '7', '6', '5'], ['4', '2', '1', '7', '9', '6', '5'], ['4', '2', '1', '7', '10', '6', '5']]
# DFA開始狀態: [['4', '2', '0', '1', '7']]
# DFA終止狀態: [['4', '2', '1', '7', '10', '6', '5']]
def draw_dfa(state_list, begin, end):
    g = Digraph('G', filename='dfa.gv', format='png')
    begin_aaa = []
    end_aaa = []
    for i in range(len(begin)):
        begin_aaa.append(" ".join(sorted(begin[i])))
    for j in range(len(end)):
        end_aaa.append(" ".join(sorted(end[j])))

    for i in range(len(state_list)):
        dicttttt = find_state(state_list[i])
        for j in para0:
            if state_list[i] != [] and dicttttt[j] != []:
                g.edge(" ".join(sorted(state_list[i])), " ".join(sorted(dicttttt[j])), label=j)
    for k in range(len(begin_aaa)):
        g.node(begin_aaa[k], color='red', shape='circle')
    for i in range(len(state_list)):
        g.node(" ".join(sorted(state_list[i])), color='blue', shape='circle')
    for j in range(len(end_aaa)):
        if end_aaa[j]!=begin_aaa[0]:
            g.node(end_aaa[j], shape='doublecircle')
        else:
            g.node(end_aaa[j], shape='doublecircle',color='red')
    g.view()


draw_dfa(state_list, dfa_begin, dfa_end)



state_dfa_other_name=[ chr(65+i) for i in range(len(state_list))]
#['A', 'B', 'C', 'D', 'E']
start_char=state_dfa_other_name[ state_list.index(dfa_begin[0])]
start_char=list(start_char)
print(start_char)
end_char=[]
for i in range(len(dfa_end)):

    s=state_dfa_other_name[state_list.index(dfa_end[i])]
    end_char.append(s)
print(end_char)


# def draw_dfa_2(state_list,state_dfa_other_name, start_char, end_char):
#     g = Digraph('G', filename='dfa.gv', format='png')
#     # begin_aaa = []
#     # end_aaa = []
#     # for i in range(len(begin)):
#     #     begin_aaa.append(" ".join(sorted(begin[i])))
#     # for j in range(len(end)):
#     #     end_aaa.append(" ".join(sorted(end[j])))
#
#     for i in range(len(state_list)):
#         dicttttt = find_state(state_list[i])
#         for j in para0:
#             if state_list[i] != [] and dicttttt[j] != []:
#                 g.edge(" ".join(sorted(state_list[i])), " ".join(sorted(dicttttt[j])), label=j)
#     for k in range(len(start_char)):
#         g.node(start_char[k], color='red', shape='circle')
#     for i in range(len(state_list)):
#         g.node(state_dfa_other_name[i], color='blue', shape='circle')
#     for j in range(len(end_char)):
#         g.node(end_char[j], shape='doublecircle')
#     g.view()
# draw_dfa_2(state_list,state_dfa_other_name,start_char,end_char)
# def min_list(state_list,dfa_end):
#     l=state_list.remove(dfa_end)
#     min_dfa_column_one=[]
#     for i in range(len(state_list)):
#         if find_state(state_list[i]) == dfa_end:
#             min_dfa.append(state_list[i])

本文來自部落格園,作者:{Zeker62},轉載請註明原文連結:https://www.cnblogs.com/Zeker62/p/15350095.html