NFA转换为DFA及DFA的化简.docx
《NFA转换为DFA及DFA的化简.docx》由会员分享,可在线阅读,更多相关《NFA转换为DFA及DFA的化简.docx(38页珍藏版)》请在冰豆网上搜索。
![NFA转换为DFA及DFA的化简.docx](https://file1.bdocx.com/fileroot1/2022-12/8/d4b6c256-69cc-473e-8c2f-1efe1eed97ed/d4b6c256-69cc-473e-8c2f-1efe1eed97ed1.gif)
NFA转换为DFA及DFA的化简
实验三
(一)NFADFA(2小时)
实验目的:
学会编程实现子集构造法
实验任务:
存储NFA与DFA,编程实现子集构造法将NFA转换成DFA
实验内容:
(1)确定NFA与DFA的存储格式,为3个以上测试NFA准备好存储文件。
NFA的存储格式:
3//转换边数
0//开始状态
3-1//结束状态集,以-1为结束标志
01a//状态转换
02a//如状态0在a输入下转换为状态2
23b//其中用#表示空转换
如测试用例:
(a)以a开头和结尾的小字字母串;a(a|b|…|z)*a|a
(b)不包含三个连续的b的,由字母a与b组成的字符串;(e|b|bb)(a|ab|abb)*
(c)(aa|b)*(a|bb)*
(2)用C或JAVA语言编写将NFA转换成DFA的子集构造法的程序。
子集构造法原理:
子集构造法主要代码:
子集构造函数:
voidchild_method()
{
intm,n;
for(m=0;m<100;m++)
for(n=0;n<100;n++)
Dtran[m][n]='#';
for(m=0;m<100;m++)
DFA[m].flag=-1;
StateS0,U;
S0.flag=0;
S0.count=1;
S0.H[0]=first;
StateT;
T=closure(S0);
T.mark=0;
T.flag=0;
DFA[numof_Dtran++]=T;
memset(useof_DFA,0,sizeof(useof_DFA));
//检查DFA中是否存在未被标记的状态
intj=check_inDFA();
intk;
while(j!
=-1)
{
useof_DFA[j]=1;
for(k=0;k{
U=closure(move(DFA[j],alpha[k]));//求闭包
//ifU不在DFA中
if(!
check_whetherin_DFA(U))//判断是否在DFA中
{
U.mark=numof_Dtran;
DFA[numof_Dtran++]=U;
}
Dtran[DFA[j].mark][U.mark]=alpha[k];
}
j=check_inDFA();//检查DFA中是否存在未被标记的状态,有返回标号,否则返回-1
}
}
闭包函数:
Stateclosure(StateT)//求闭包
{
stackSTACK;
Statetemp;
inti,j,k;
for(i=0;i{
STACK.push(T.H[i]);
temp.H[i]=T.H[i];
}
temp.count=T.count;
/*temp.flag=0;*/
temp.mark=T.mark;
while(!
STACK.empty())
{
intt=STACK.top();
STACK.pop();
//搜索状态t通过一个或多个空字符到达的状态
intsearch_result[100];
intnum;
arriveBynone(t,search_result,num);
for(j=0;j{
if(!
check(search_result[j],temp))
{
temp.H[temp.count++]=search_result[j];
STACK.push(search_result[j]);
}
}
}
for(k=0;k{
if(f[temp.H[k]]==1)
{
temp.flag=1;
break;
}
if(f[temp.H[k]]==0)
{
temp.flag=0;
break;
}
}
sort(temp.H,temp.H+temp.count);
for(i=0;i{
if(temp.count!
=DFA[i].count)
continue;
sort(DFA[i].H,DFA[i].H+DFA[i].count);
for(j=0;j{
if(DFA[i].H[j]!
=temp.H[j])
break;
}
if(j>=DFA[i].count)
temp.mark=DFA[i].mark;
}
returntemp;
}
检查未标记状态函数:
intcheck_inDFA()//检查DFA中是否存在未被标记的状态,有则返回标号,否则返回-1
{
inti;
for(i=0;i{
if(!
useof_DFA[i])
returni;
}
return-1;
}
检查一个状态是否在DFA中的函数:
boolcheck_whetherin_DFA(StateT)
{
inti,j;
sort(T.H,T.H+T.count);
for(i=0;i{
if(T.count!
=DFA[i].count)
continue;
sort(DFA[i].H,DFA[i].H+DFA[i].count);
for(j=0;j{
if(DFA[i].H[j]!
=T.H[j])
break;
}
if(j>=DFA[i].count)
returntrue;
}
if(i>=numof_Dtran)
returnfalse;
else
returntrue;
}
(3)测试。
利用步骤1中所给的测试用例来验证。
a)以a开头和结尾的小字字母串;a(a|b|…|z)*a|a
运行结果及分析:
结果分析:
首先将读入的NFA信息输出:
然后将转换后的DFA输出,其中2号状态表示转换后的∅状态,即无效状态。
状态解释:
0:
1表示用0状态表示NFA中的1状态
1:
23表示用1状态表示{2,3}状态集合。
2:
表示用2状态表示∅状态,即无效状态。
3:
2表示用3状态表示NFA中的状态2。
最后,判断字符串是否能由此DFA得到:
显然,从以上三个例子可以看出,以a开头且以a结尾的字符串可以由该DFA得到而以b开头或结尾的字符串则无法由该DFA得到。
所以得到的DFA正确。
b)不包含三个连续的b的,由字母a与b组成的字符串;(e|b|bb)(a|ab|abb)*
同样可以得到结果及字符串的判断:
显然,得到的DFA可以接受连续最多2个b,到3个就不能接受,与NFA一样,正确。
c)(c)(aa|b)*(a|bb)*
显然,验证结果均正确。
实验三
(二)DFA化简(2小时)
实验目的:
学会编程实现等价划分法化简DFA。
实验任务:
先完善DFA,再化简DFA。
实验内容:
(1)准备3个以上测试DFA文件。
测试文件格式即DFA存储格式说明:
2//字符个数
ab//字符
3//非终结状态个数
1//终止状态个数
012//非终止状态
3//终止状态
12//状态转换表
02//如第二行表示状态1在a输入下转换成状态0,在b输入下转换成状态2
03
32
测试文件1:
测试文件2:
测试文件3:
(2)用C或JAVA语言编写用等价划分法化简DFA的程序。
DFA最小化原理:
●确定型有穷自动机(DFA):
有有穷状态集合和输入符号集合,转移函数,初始状态,以及一个终结状态集合。
表示为
。
DFA的状态图:
用箭头来表示状态间的转换,用圆圈+文字表示状态,用一个同心圆表示终结状态,用start表示开始状态。
状态表:
可以想象。
●DFA状态最小化:
最小有限自动机,是指满足下述条件的确定有限自动机:
⑴没有无用状态(无用状态已删除);⑵没有等价状态(等价状态已合并)。
Ⅰ.删除无用状态算法
无用状态是指自动机从开始态出发,对任何符号串都不能到达的状态。
判别算法:
构造有用状态集Qus
⑴设q0为开始态,则令q0∈Qus;
⑵若qi∈Qus且有d(qi,a)=qj则令qj∈Qus;
⑶重复执行⑵,直到Qus不再增大为止。
⑷从状态集Q中,删除不在Qus中的所有状态。
Ⅱ.合并等价状态算法
等价状态:
两个状态i,j等价,当且仅当满足下面两个条件:
1必须同是结束态,或同不是结束态;
2对所有字母表上符号,状态i,j必变换到等价状态。
划分不等价状态集
⑴初始,把状态集Q化分成两个不等价子集:
Q1(结束状态集),Q2(非结束状态集);
⑵把每个Qi再划分成不同的子集,条件是:
对同一Qi中两个状态i和j,若对字母表中的某个符号,变换到已划分的不同的状态集中,则i和j应分离:
如d(i,a)∈Qm,d(j,a)∈Qn且m≠n
⑶重复步骤⑵,直到再不能划分为止;
⑷合并最终划分的每个子集中的各状态(合而为一)。
部分函数代码:
判断等价状态:
while(endflag)
{intflag;
for(i=0;i<(temp+1);i++)//对每个等价类集合
for(intj=0;j{flag=0;
if(sta[j].lastf==i)
{
for(intk=0;k/若是在同一个等价类,继续测试if(sta[k].lastf==sta[j].lastf&&sta[k].newf==sta[j].newf)/
for(intt=0;t{ints1,s2;
s1=move[j][t];
s2=move[k][t];
if(sta[s2].lastf!
=sta[s1].lastf)//出现分歧
{sta[k].newf=f+1;
flag=1;
break;
}
}
}
if(flag==1)
f++;
}
for(i=0;iif(sta[i].lastf==sta[i].newf)
;
else
break;
if(i==n)//若所有的状态等价类编号不再变化,退出while循环
endflag=0;
for(i=0;ista[i].lastf=sta[i].newf;
temp=f;
}//循环结束
判断是否存在不可达状态:
for(i=0;i{ints0=sta[0].newf;
sta[s0].kd=1;
for(intj=0;j{sta[move[s0][i]].kd=1;
s0=move[s0][i];
}
}
intunreach=0;
for(i=0;iif(sta[i].kd==1)
;
else//containunreachblestate
{unreach=1;
cout<<"newstate"<
for(intj=0;jmove[i][j]=-1;
}
(3)测试
测试1:
输入文件已在步骤1中进行过说明。
运行结果及分析:
从输入文件和状态转换图可以看出状态0和状态2是等价状态
可以看出程序将0和2号状态合并,可是建立新的状态转移的时候好像有点问题,没有解决。
测试2:
(测试文件格式在步骤1中)
测试3:
(测试文件格式在步骤1中)
经验证,状态分类是正确的,最小化之后的状态转移有点不对。
实验心得:
通过本次实验,学会了如何将一个非确定型状态机转换成确定型状态机以及如何将
一个确定型的有穷自动机化简成一个状态最小的有穷自动机。
更加深刻地理解了有穷自动机、等价状态等概念,同时也遇到了很多困难,例如,如何将一个自动机用C++语言来表示,如何将一个图形改成抽象的语言描述等。
总之,对课本的知识有了进一步的理解,收获颇多。
附:
两个实验的代码+工程
//NFA转为DFA.cpp:
定义控制台应用程序的入口点。
#include"stdafx.h"
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
structedge{
intstart,end;
charc;
}E[100],Ekong[100];//E保存所有的边,Ekong保存转换字符为空的边
structState{
intH[100];//状态集合
intcount;//状态集合中的元素个数
intflag;//是否是接受状态
intmark;//状态编号
};
intn;//n:
边数
intnk=0;//空字符转换的边数
intfirst,accept;//开始状态,接受状态
charalpha[100];//输入字母表,#代表空串
intnumof_char=0;//字母表中的字符个数
intuseof_char[256];//该转换字符是否用过
intf[200];//状态属性标志:
0表示始态,1表示接受态,-1表示中间态
StateDFA[100];//DFA状态集
intuseof_DFA[100];//标志构造出来的状态是否已存在
intnumof_Dtran=0;//最后得到的DFA中的状态数
charDtran[100][100];//DFA状态转换表
voidinput()
{
ifstreamfin("input3.txt",ios:
:
in);
inti,s,e;
charch;
fin>>n;
cout<<"此状态转换有"<fin>>first;
cout<<"开始状态为:
"<memset(f,-1,sizeof(f));
memset(useof_char,0,sizeof(useof_char));
f[first]=0;
cout<<"接受状态集:
";
//cin>>accept;
fin>>accept;
while(accept!
=-1)
{
f[accept]=1;
cout<fin>>accept;
}
cout<<"\n\n起点,终点,转换字符('#'表示空字符):
"<intk=0;
for(i=0;i{
fin>>s>>e>>ch;
cout<
E[i].start=s;
E[i].end=e;
E[i].c=ch;
if(ch!
='#'&&!
useof_char[ch])
{
alpha[numof_char++]=ch;
useof_char[ch]=1;
}
if(ch=='#')
{
Ekong[nk].start=s;
Ekong[nk].end=e;
Ekong[nk].c=ch;
nk++;
}
}
}
Statemove(StateT,chars)//c!
='#'
{
Statetemp;
temp.count=0;
/*temp.flag=0;*/
temp.mark=T.mark;
inti,j=0,k=0;
for(i=0;i{
j=0;
while(j{
if(E[j].start==T.H[i]&&E[j].c==s)
{
temp.H[temp.count++]=E[j].end;
}
j++;
}
}
returntemp;
}
voidarriveBynone(intt,intresult[],int&num)//搜索状态t通过一个或多个空字符到达的状态,结果存在result中
{
intk=0;
intm=0;
num=0;
stackS;
S.push(t);
intj;
while(!
S.empty())
{
j=S.top();
S.pop();
m=0;
while(m{
if(Ekong[m].start==j)
{
result[num++]=Ekong[m].end;
S.push(Ekong[m].end);
}
m++;
}
}
}
boolcheck(inti,StateT)//判断状态i是否在T中
{
intj;
for(j=0;j{
if(T.H[j]==i)
returntrue;
}
returnfalse;
}
Stateclosure(StateT)//求闭包
{
stackSTACK;
Statetemp;
inti,j,k;
for(i=0;i{
STACK.push(T.H[i]);
temp.H[i]=T.H[i];
}
temp.count=T.count;
/*temp.flag=0;*/
temp.mark=T.mark;
while(!
STACK.empty())
{
intt=STACK.top();
STACK.pop();
//搜索状态t通过一个或多个空字符到达的状态
intsearch_result[100];
intnum;
arriveBynone(t,search_result,num);
for(j=0;j{
if(!
check(search_result[j],temp))
{
temp.H[temp.count++]=search_result[j];
STACK.push(search_result[j]);
}
}
}
for(k=0;k{
if(f[temp.H[k]]==1)
{
temp.flag=1;
break;
}
if(f[temp.H[k]]==0)
{
temp.flag=0;
break;
}
}
sort(temp.H,temp.H+temp.count);
for(i=0;i{
if(temp.count!
=DFA[i].count)
continue;
sort(DFA[i].H,DFA[i].H+DFA[i].count);
for(j=0;j{
if(DFA[i].H[j]!
=temp.H[j])
break;
}
if(j>=DFA[i].count)
temp.mark=DFA[i].mark;
}
returntemp;
}
intcheck_inDFA()//检查DFA中是否存在未被标记的状态,有则返回标号,否则返回-1
{
inti;
for(i=0;i{
if(!
useof_DFA[i])
returni;
}
return-1;
}
boolcheck_whetherin_DFA(StateT)
{
inti,j;
sort(T.H,T.H+T.count);
for(i=0;i{
if(T.count!
=DFA[i].count)
continue;
sort(DFA[i].H,DFA[i].H+DFA[i].count);
for(j=0;j{
if(DFA[i].H[j]!
=T.H[j])
break;
}
if(j>=DFA[i].count)
returntrue;
}
if(i>=numof_Dtran)
returnfalse;
else
returntrue;
}
voidchild_method()
{
intm,n;
for(m=0;m<100;m++)
for(n=0;n<100;n++)
Dtran[m][n]='#';
for(m=0;m<100;m++)
DFA[m].flag=-1;
StateS0,U;
S0.flag=0;
S0.count=1;
S0.H[0]=first;
StateT;
T=closure(S0);
T.mark=0;
T.flag=0;
DFA[numof_Dtran++]=T;
me