编译原理实验——将NFA转化为DFA

本文介绍了一种将非确定有限自动机(NFA)转换为确定有限自动机(DFA)的算法,包括计算ε-closure集及转换函数的实现方法,并详细描述了具体的实验步骤。

转自http://blog.youkuaiyun.com/lingyu940/article/details/5070137


将NFA转化为DFA


1.实验目的


输入: 非确定有限(穷)状态自动机。

输出: 确定化的有限(穷)状态自动机

2.实验原理

采用子集对NFA转DFA。

(1) 若NFA的全部初态为S1,S2,…,Sn,则令DFA的初态为:

S=[S1,S2,…,Sn],

其中方括号用来表示若干个状态构成的某一状态。

(2) 设DFA的状态集K中有一状态为[Si,Si+1,…,Sj],若对某符号a∈∑,在NFA中有F({ Si,Si+1,…,Sj },a)={ Si’,Si+1’,…,Sk’ }

则令F({ Si,Si+1,…,Sj },a)={ Si’,Si+1’,…,Sk’ }为DFA的一个转换函数。若[ Si’,Si+1’,…,Sk‘ ]不在K中,则将其作为新的状态加入到K中。

(3) 重复第2步,直到K中不再有新的状态加入为止。

(4) 上面得到的所有状态构成DFA的状态集K,转换函数构成DFA的F,DFA的字母表仍然是NFA的字母表∑。

(5) DFA中凡是含有NFA终态的状态都是DFA的终态。

还可以采用另一种操作性更强的描述方式。首先给出两个相关定义。

假设I是NFA M状态集K的一个子集(即I∈K),则定义ε-closure(I)为:

(1) 若Q∈I,则Q∈ε-closure(I);

(2) 若Q∈I,则从Q出发经过任意条ε弧而能到达的任何状态Q’,则Q’∈ε-closure(I)。

状态集ε-closure(I)称为状态I的ε闭包。

假设NFA M=(K,∑,F,S,Z),若I∈K,a∈∑,则定义Ia=ε-closure(J),其中J是所有从ε-closure(I)出发,经过一条a弧而到达的状态集。

NFA确定化的实质是以原有状态集上的子集作为DFA上的一个状态,将原状态间的转换为该子集间的转换,从而把不确定有限自动机确定化。经过确定化后,状态数可能增加,而且可能出现一些等价状态,这时就需要简化

3.实验内容

⑴ 实现计算闭包closure(I)的算法;

⑵ 实现转换函数move(q,a)的算法;

⑶ 输出NFA转DFA的结果。



DFA和NFA存储使用的数据结构为图中的邻接链表。

typedef struct ArcNode{

int adjvex; //该弧所指向的顶点的位置

char info; //权

struct ArcNode *nextarc; //指向下一条弧的指针

}ArcNode;

typedef struct VNode{

char data; //顶点信息

ArcNode *firstarc; //指向第一条依附该顶点的弧的指针

}VNode;

每个链表的第一个数据单元为N(D)FA的状态,后边的结点存储内容有指向的边和边上的权值。

例如课本图4.3的NFA M表示如下:




4.实验心得

1.一般来说,我们接触到的N(D)FA图的边数比较少,所以在选取数据结构存储时,选择邻接链表,相对于使用二维或三维数组,存储效率得到了提高。

2.在求e_closure集合时,因为是任意个空转移,因此使用深度遍历的方法,先求出一个状态的e_closure集,再用循环求出状态集的e_closure集
### 编译原理中的正规式换为NFADFA #### 将正规式换为NFA的方法 在编译原理中,通常采用三步走的方式处理正规式的换问题。首先,将给定的正则表达式(即正规式)转化为非确定有限状态自动机(NFA),这是因为相对于直接构建DFA而言更为简单直观[^3]。 对于具体的化过程,存在一些基本规则用于指导从简单的模式片段创建对应的NFA组件: - 对于单个字符`a`,可以建立一个只有两个节点的状态移图,其中一条由初始态指向接受态的边标记着该字符; - 并运算(`|`)对应两条分别通往不同子路径的选择分支; - 连接操作意味着顺序执行多个模式部分所代表的动作序列; - 星闭包(*)表示零次或多次重复前导元素/组的内容。 这些原则能够帮助理解更复杂的结构是如何逐步组装起来形成完整的NFA模型的[^2]。 ```python class State: def __init__(self, is_accept=False): self.transitions = {} self.is_accept = is_accept def re_to_nfa(pattern): stack = [] for char in pattern: if char == '|': # Or operation b, a = stack.pop(), stack.pop() new_start = State() new_end = State(True) new_start.transitions[''] = {a, b} a.transitions[''].add(new_end) b.transitions[''].add(new_end) stack.append((new_start, new_end)) elif char == '*': # Kleene star state = stack.pop()[0] new_start = State() new_end = State(True) new_start.transitions[''] = {state, new_end} state.transitions[''].add(new_end) stack.append((new_start, new_end)) else: # Single character start_state = State() end_state = State(True) start_state.transitions[char] = {end_state} stack.append((start_state, end_state)) return stack[-1][0] pattern = "ab*c" nfa_start_state = re_to_nfa(pattern) ``` 这段Python代码展示了如何基于上述提到的原则来实现一个简易版的正则表达式NFA的功能函数 `re_to_nfa()` 。此函数接收字符串形式的正则表达式作为参数,并返回其相应的NFA起始状态对象。 #### NFADFA换方法 一旦获得了针对特定正规式的NFA之后,则可以通过子集构造算法将其进一步变为等价的DFA。这一过程中涉及到的主要概念包括ε-closure(空移封闭集合)以及move(T,a)(当读入符号'a'时T集中各状态所能到达的新状态集合)[^4]。 以下是简化版本的伪码展示如何利用这两个辅助函数完成整个变流程: ```python from collections import deque def epsilon_closure(states_set, nfa_states_dict): closure = set(states_set) queue = deque(closure.copy()) while queue: current_state_id = queue.popleft() if '' not in nfa_states_dict[current_state_id].transitions: continue next_states_ids = nfa_states_dict[current_state_id].transitions[''] for s in next_states_ids.difference(closure): closure.add(s) queue.append(s) return frozenset(closure) def move(states_frozenset, symbol, nfa_states_dict): result = set() for sid in states_frozenset: if symbol in nfa_states_dict[sid].transitions: result.update(nfa_states_dict[sid].transitions[symbol]) return frozenset(result) def nfa_to_dfa(nfa_initial_state, alphabet, nfa_states_dict): dfa_states = {} # Maps frozen sets of NFA states to DFA states. transitions = {} unprocessed_sets = [epsilon_closure({nfa_initial_state}, nfa_states_dict)] processed_sets = [] accept_states = set() while unprocessed_sets: current_set = unprocessed_sets.pop() processed_sets.append(current_set) if any(state.id in accept_states for state in current_set): accept_states.add(id(dfa_states[current_set])) for letter in alphabet: reachable_by_letter = move(current_set, letter, nfa_states_dict) ec_reachable_by_letter = epsilon_closure(reachable_by_letter, nfa_states_dict) if ec_reachable_by_letter and (ec_reachable_by_letter not in processed_sets + unprocessed_sets): unprocessed_sets.insert(0, ec_reachable_by_letter) if current_set in dfa_states.keys(): from_state_id = id(dfa_states[current_set]) else: from_state_id = len(dfa_states)+1 dfa_states[current_set] = f"D{len(dfa_states)}" if ec_reachable_by_letter in dfa_states.keys(): to_state_id = id(dfa_states[ec_reachable_by_letter]) else: to_state_id = None transitions[(from_state_id, letter)] = to_state_id initial_state = list(dfa_states.values())[list(dfa_states).index(frozenset([nfa_initial_state]))] return {'states':dfa_states,'initial_state':initial_state,'accepting_states':accept_states,'transition_function':transitions} alphabet = ['a', 'b', 'c'] # Assuming we have already defined the necessary dictionaries mapping between IDs and actual States objects... converted_dfa_info = nfa_to_dfa(initial_state=nfa_start_state, alphabet=alphabet, nfa_states_dict=all_nfa_states) ``` 这里给出了一套较为详细的Python风格伪代码用来说明怎样把之前得到的那个NFA实例化成最终的目标——DFA。注意这里的输入还包括了一个字母表(alphabet),它定义了合法输入字符范围;另外还需要提供所有参与计算的NFA状态及其关联信息组成的字典(all_nfa_states)以便查询使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值