整理一下上学期写的代码,关于自动机的相关算法实现。
实现基于有穷自动机的形式化表示,可视化没什么思路就没写。
完整代码已上传至github https://github.com/huiluczP/finiteAutomata
直接想用的话test.py脚本中有示例,导入文件直接用就行。
实现功能
NFA相关:
- 利用五元组,创建NFA模型
- 输入字符串,判定是否被该NFA模型接受
DFA相关:
- 利用五元组,创建DFA模型
- 输入字符串,判断是否被该DFA模型接受
- NFA转DFA
- DFA最小化
- RL与DFA转换
包含三个文件,DFA.py,NFA.py,test.py。
数据结构
形式化表示,有穷自动机都是五元组。
DFA数据结构:
class DFA:
states = [] # 所有状态
input_symbols = [] # 字母表
start_state = "q0" # 初始状态,仅有一个
final_states = [] # 终结状态
trans = {
} # 转移函数,字典,包括启示状态,字符与终点{'q1':{'a':'q2', 'b':'q3'}}
- 初始状态start_state
由于FA只有一个初始状态,所以利用字符串表示。如q0。 - 终结状态final_states
多个终结状态,利用列表list存放。如[’q1’] - 所有状态states
利用list存放。如[‘q0’,’q1’,’q2’] - 字母表input_symbols
利用list存放。如[‘a’,’b’] - 状态转移 trans
利用字典进行存放,即为key-value值键对存放。字典第一级key为初始状态,对应的值存放转移规则。具体的转移规则也由字典存放,该层字典的key为转移接受的字符,值为转移后的结果。由于DFA转移结果确定,所以利用字符串存放。如状态q1接受a转移到状态q2,表示为{“q1”: {“a”: “q2”}}。
NFA数据结构:
class NFA:
states = [] # 所有状态
input_symbols = [] # 字母表
start_state = "q0" # 初始状态,仅有一个
final_states = [] # 终结状态
trans = {
} # 转移函数,字典,包括启示状态,字符与终点{“q1”: {“a”: [“q2”,”q3”]}}
NFA数据结构除转移规则外都相同,由于转移规则结果不确定,利用列表list存放转移后的状态集合。如{“q1”: {“a”: [“q2”,”q3”]}}。
算法具体实现
NFA判断字符串是否被接受
实现在NFA.py中
代码分析
计算闭包
def _cal_closure(self, state):
# 计算状态state的空闭包
closure_list = [state]
middle_state = deque()
middle_state.append(state)
while len(middle_state) > 0:
current_state = middle_state[0]
if current_state in self.trans.keys():
if "e" in self.trans[current_state].keys():
for s in self.trans[current_state]["e"]:
if s not in closure_list:
closure_list.append(s)
middle_state.append(s)
middle_state.popleft()
return closure_list
由于NFA存在空转移,所以每个字符的输入到达的状态可能有多个,则需要计算对应的空闭包。状态的空闭包为对应状态加上空规则的终点状态集合。代码创建一个队列middle_state,队列首先入队输入状态s,之后找到以s为起点的空转移规则,将结果放入队列,同时队列第一个状态出队列。以此循环,当middle_state为空时结束计算,返回闭包。
获取下一个状态集合
def _get_next_states(self, current_states, input_char):
# 由于nfa,则输入的状态和到达的状态都不唯一
next_states = []
for c_state in current_states:
if c_state in self.trans.keys():
if input_char in self.trans[c_state].keys():
end_states = self.trans[c_state][input_char]
for s in end_states:
s_closure = self._cal_closure(s)
for s_c in s_closure:
if s_c not in next_states:
next_states.append(s_c)
print(current_states, end="")
print(" -> ", end="")
print(next_states)
return next_states
遍历转移规则集合,找到对应初始状态与输入字符的转移,并计算目标状态的空闭包集合。该状态集合即为下一个状态的集合。
判断是否存在终结状态
def _have_final_states(self, current_states):
# 判断是否含有终结状态
for c in current_states:
if c in self.final_states:
return True
return False
判断遍历完字符后的状态集合,若其中有终结状态,则接受该字符串,否则不接受。
示例分析
输入如图所示的NFA。
t_states = ["q1", "q2", "q3"]
t_input = ["a", "b"]
t_trans = {
"q1":
{
"e": ["q3"],
"b": ["q2"]
},
"q2":
{
"a": ["q2", "q3"],
"b": ["q3"]
},
"q3":
{
"a": ["q1"],
},
}
t_start = "q1"
t_final = ["q1"]
nfa = NFA(t_states, t_input, t_trans, t_start, t_final)
输入可接受字符串baba,获得结果:
['q1'] -> ['q2']
['q2'] -> ['q2', 'q3']
['q2', 'q3'] -> ['q3']
['q3'] -> ['q1', 'q3']
被接受
输入不可接受字符串babaab,获得结果:
['q1'] -> ['q2']
['q2'] -> ['q2', 'q3']
['q2', 'q3'] -> ['q3']
['q3'] -> ['q1', 'q3']
['q1', 'q3'] -> ['q1', 'q3']
['q1', 'q3'] -> ['q2']
不被接受
DFA判断字符串是否被接受
实现在DFA.py中
代码分析
def read_input(self, input_str):
# 遍历输入字符串字符,进行状态转移,若最终状态为终结状态,输出接收,否则输出错误
current_state = self.start_state
# 确认字符可靠性
for c in input_str:
if c not in self.input_symbols:
print("输入字符串包括非法字符")
return False
# 遍历
for c in input_str:
current_state = self