Python实现DFA确定型有穷自动机和NFA非确定型有穷自动机相关算法

整理一下上学期写的代码,关于自动机的相关算法实现。
实现基于有穷自动机的形式化表示,可视化没什么思路就没写。

完整代码已上传至github https://github.com/huiluczP/finiteAutomata
直接想用的话test.py脚本中有示例,导入文件直接用就行。

实现功能

NFA相关:

  1. 利用五元组,创建NFA模型
  2. 输入字符串,判定是否被该NFA模型接受

DFA相关:

  1. 利用五元组,创建DFA模型
  2. 输入字符串,判断是否被该DFA模型接受
  3. NFA转DFA
  4. DFA最小化
  5. 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'}}
  1. 初始状态start_state
    由于FA只有一个初始状态,所以利用字符串表示。如q0。
  2. 终结状态final_states
    多个终结状态,利用列表list存放。如[’q1’]
  3. 所有状态states
    利用list存放。如[‘q0’,’q1’,’q2’]
  4. 字母表input_symbols
    利用list存放。如[‘a’,’b’]
  5. 状态转移 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
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值