DFA与NFA的区别,由正规表达式构造DFA,以及DFA的相关化简

目录

1.DFA(确定有穷自动机)和NFA(不确定的有穷自动机)的区别 

2.根据五元组构建DFA和NFA        

3.由正规式到DFA

首先讲如何从正规式到NFA

如何从NFA到DFA

2.DFA的化简


1.DFA(确定有穷自动机)和NFA(不确定的有穷自动机)的区别 

(1)DFA只能单值映射,而NFA则能推出多个值:

DFA

NFA

(2)初态不同:

DFA中有且仅有一个初态。这个初态是自动机开始处理输入串时的初始状态,从这个状态开始进行状态转移,直到输入串处理完毕。
在NFA中,可以有多个初态。NFA允许有多个状态作为初始状态,即可以从多个状态同时开始处理输入串,从而导致在给定输入下存在多条可能的路径。

(3)转换过程不同:

NFA的状态转换过程中可以有空串,如下图即为NFA:

这就导致了一个问题:开始之后,在给出字符a或b之前,我们能够确定当前是处于1状态还是2状态吗?很显然,我们是无法确定的,因此才被称为不确定的有穷自动机,因为空串的存在,我们无法确定当前的具体状态是什么,NFA便于定理的证明,但是DFA便于识别,利于计算机实现:

NFA的不确定表现我们可以概括为:1.多值映射        2.带空转移

所以我们通常要将NFA转换为DFA

2.根据五元组构建DFA和NFA        

 例题1:

这里的五元组分别对应有限状态集,有穷输入字母表,映射关系,初态和终态

例题2:

3.由正规式到DFA

正规式--->NFA---->DFA

首先讲如何从正规式到NFA

转换规则:

例题1:这里圆圈里面的命名是随意的,只要能区别开就可以了

例题2:

如何从NFA到DFA

NFA---->状态转换表---->状态转换矩阵---->DFA

如下例题:

以上NFA的转换表如下图所示:

这里的"I"是从x出发的状态,"Ia"表示I集合中的字符经过a的状态的集合(注:除了空字符(\varepsilon)外,其他字符只能走一段)

规则:

第一行第一个:若经过的是\varepsilon(空串),那么就经过空串后到达的字符加入到集合中,如果没有经过\varepsilon空串,就不到达。

第二列在第一列中的字符,经过a的都加入进去。例如第一列为{X,1,2},那么

X:X经过的是\varepsilon(空串),没有经过a

1:1经过了a,加入1,并且将a后面经过\varepsilon(空串)的字符全部加入进去,即{1,2}

2:2经过了a到达3,可以加入进去,即{2,3},这里只能经过一个a:

例如:这里那么1只能推出3,不能再继续推出2了

所以注意:相应的字符不能连续,但是连续\varepsilon(空串)后的字符可以加入进去

第三列:与第二列同理,只是字符a变为了字符b

最后得到这里的第一行:

这里的第一列的每一行就是列举上一行中出现的集合,例如第二列,列举的就是上一行中出现的红框的集合

就拿I={1,2,3}具体说:

由图,1经过a的状态有:1,2,2经过a的状态有3,3经过a的状态有5,6,Y(因为5后面接的就是\varepsilon串),所以I_{a}={1,2,3,5,6,Y}

以此类推就能得到转换表,再将相同的集合表示出来,方法就是将第一列从0开始排序,其他列与第一列相同的字符,就赋上相应的序号

就可以进一步得到转换矩阵

再根据状态转换矩阵可得图DFA

注:这个图怎么判断这个状态是不是一个终态(一个圈还是两个圈),那么我们只需要看状态转换表

表中含有Y的集合,就是终态,需要画两个圈

2.DFA的化简

这里终态和非终态的状态分别为终态={3,4,5,6},非终态={0,1,2}

对于非终态{0,1,2}:

将{0,1,2}分别输入a,即{0,1,2}a,通过状态转换矩阵可知,{0,1,2}a={1,3},{1,3}对于{0,1,2}而言,不是包含关系,所以

将得到1的状态和得到3的状态分开:

{0,2}-->{1},{1}--->{3}

再对{0,2}输入b的状态:

{0,2}b--->{2,4},{2,4}不包含在{0,2}中,所以{0}--->{2},{2}---->{4}

 对于终态{3,4,5,6}:

{3,4,5,6}a={3,6},包含关系

{3,4,5,6}b={4,5},包含关系

对于非终态有{0}{1}{2}状态,对于终态有{3,4,5,6}状态,将他视为状态{3},那么

这里还是根据状态转换矩阵画,只是看到{3,4,5,6}都指向状态{3}

在C语言中,实现DFA(确定有限自动机)状态转换表的关键在于维护状态集和转移函数。这里以简单的子集构造法为例,假设我们有一个NFA(非确定有限自动机),我们可以逐步构建其对应的DFA。下面是一个基本的C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> // 定义状态和字符的结构体 typedef struct { char state[MAX_NUM]; // 存储当前状态集 char next_state[MAX_NUM][ALPHABET_SIZE]; // 转移矩阵 } DFAState; // 帮助函数,用于合并两个状态集 void merge_states(DFAState *dfa, DFAState *states, size_t state1, size_t state2) { for (int i = 0; i < ALPHABET_SIZE; ++i) { if (states[state1].state[i] == 1 && states[state2].state[i] == 1) dfa->next_state[state1][i] = 1; } } // 根据输入字符从当前状态集转移到新的状态集 void transition(DFAState *dfa, char input_char, DFAState *nfa_states) { for (size_t i = 0; i < MAX_NUM; ++i) { if (memcmp(dfa->state, nfa_states[i].state, sizeof(char)*MAX_NUM) == 0) { memcpy(dfa->next_state, nfa_states[i].next_state, sizeof(char)*MAX_NUM*ALPHABET_SIZE); return; } } printf("Error: No matching state found.\n"); // 处理未找到匹配状态的情况 } // 主函数,从NFA开始构造DFA void construct_DFA(DFAState *dfa, DFAState *nfa_states) { // 初始化DFA为空 memset(dfa->state, 0, sizeof(char)*MAX_NUM); dfa->next_state[0][EPSILON] = 1; // 添加初始状态的ε转移 while (true) { bool done = true; for (size_t i = 0; i < MAX_NUM; ++i) { if (!dfa->state[i]) continue; // 如果状态集为空,跳过 for (size_t j = 0; j < MAX_NUM; ++j) { if (nfa_states[j].state[0] == 1) { // 如果有新的状态加入 done = false; merge_states(dfa, nfa_states, i, j); } } } if (done) break; // 如果没有新的状态组合,结束循环 // 更新状态集 for (size_t i = 0; i < MAX_NUM; ++i) { if (dfa->state[i]) transition(dfa, EPSILON, nfa_states); // ε转移 } } } // 测试用例 int main() { // 假设已有的NFA转换表和状态集... DFAState nfa_states[MAX_NUM]; // ...省略初始化和填充过程... DFAState dfa; construct_DFA(&dfa, nfa_states); // 使用构造好的DFA进行模式匹配 // ...省略匹配逻辑... return 0; } ``` 这段代码展示了基本的DFA构建过程,实际应用中可能需要根据具体的NFA结构和状态集调整细节。注意,这个例子仅适用于有限状态集和字符集大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值