非确定有限自动机的确定化分析.docx
《非确定有限自动机的确定化分析.docx》由会员分享,可在线阅读,更多相关《非确定有限自动机的确定化分析.docx(19页珍藏版)》请在冰豆网上搜索。
![非确定有限自动机的确定化分析.docx](https://file1.bdocx.com/fileroot1/2023-8/3/585802e4-c88a-4aa7-9286-0077cd42a5ad/585802e4-c88a-4aa7-9286-0077cd42a5ad1.gif)
非确定有限自动机的确定化分析
课程设计报告
课程:
编译原理
学号:
姓名:
班级:
教师:
时间:
2014.5--2014.6.20
计算机科学与技术系
设计名称:
非确定有限自动机的确定化
设计内容、目的与要求:
设计内容:
编程实现对输入NFA转换成DFA输出的功能。
设计要求:
输入非确定的有限自动机,输出确定化的有限自动机。
设计目的:
实现NFA到DFA的转化。
计划与进度安排:
2014.5.26~2014.5.30预设计的基础上,进一步查阅资料,完善设计方案,形成
书面材料。
2014.5.31~2014.6.10设计总体方案,构建绘制流程框图,编写代码,上机调试。
2014.6.11~2014.6.17测试程序,优化代码,增强功能,撰写设计报告。
2014.6.18~2014.6.20继续测试代码、检查设计报告,准备参加答辩,然后根据
教师反馈意见,修改、完善设计报告。
设计过程、步骤(可加页):
1.需求分析
由于很多计算机系统都配有多个高级语言的编译程序,对有些高级语言甚至配置了几个不同性能的编译程序。
从功能上看,一个编译程序就是一个语言翻译程序。
语言翻译程序把源语言书写的程序翻译成目标语言的等价程序。
经过编译程序的设计可以大大提高学生的编程能力。
编译程序的工作过程通常是词法分析、语法分析、语义分析、代码生成、代码优化。
由于现在有很多词法分析程序工具都是基于有穷自动机的,而词法分析又是语法分析的基础,所以我们有必要进行有穷自动机的确定化和最小化。
编译程序的这些过程的执行先后就构成了编译程序的逻辑结构。
有穷自动机(也称有限自动机)作为一种识别装置,它能准确地识别正规集,即识别正规文法所定义的语言和正规式所表示的集合,引入有穷自动机这个理论,正是为词法分析程序的自动构造寻找特殊的方法和工具。
正规表达式与自动机理论在词法构造乃至整个编译器构造过程中起着至关重要的作用,同时它们被广泛应用于计算机科学的各个领域,它们与计算机其它学科之间也有着很大的联系。
2.设计需知
2.1NFA和DFA的概念
NFA(nondeterministicfinite-stateautomata)即非确定有限自动机,一个非确定的有限自动机NFAM’是一个五元式:
NFAM’=(S,Σ∪{ε},δ,S0,F)
其中S—有限状态集
Σ∪{ε}—输入符号加上ε,即自动机的每个结点所射出的弧可以是Σ中一个字符或是ε.
S0—初态集F—终态集
δ—转换函数S×Σ∪{ε}→2S
(2S--S的幂集—S的子集构成的集合)
DFA(deterministicfinite-stateautomata)即确定有限自动机,一个确定的有限自动机DFAM是一个五元式:
M=(S,Σ,δ,S0,Z)
其中:
S—有限状态集
Σ—输入字母表
δ—映射函数(也称状态转换函数)
S×Σ→S
δ(s,a)=S’,S,S’∈S,a∈Σ
S0—初始状态S0∈S
Z—终止状态集ZS
2.2NFA和DFA之间的联系
在非确定的有限自动机NFA中,由于某些状态的转移需从若干个可能的后续状态中进行选择,故一个NFA对符号串的识别就必然是一个试探的过程。
这种不确定性给识别过程带来的反复,无疑会影响到FA的工作效率。
而DFA则是确定的,将NFA转化为DFA将大大提高工作效率,因此将NFA转化为DFA是有其一定必要的。
3.设计方案
(1)如果读入NFA,则将其转化为DFA并最小化,输入测试字符串,输出测试结果。
(2)如果读入DFA,则直接将其最小化,输入测试字符串,输出测试结果。
4.DFA实现原理
4.1NFA转换成等价的DFA
将NFA转换成等价的DFA里,NFA到相应的DFA的构造的基本思想是让DFA的每一个状态对应NFA的一组状态。
也就是说让DFA使用它的状态去记录在NFA读入一个输入符号后可能达到的所有状态,在读入输入符号之后,DFA处在a1a2......an那样一个状态,该状态表示这个NFA的状态的一个子集T,T是从NFA的开始状态沿着某个标记为a1a2......an可以达到的那些状态构成的。
对状态集合I的几个有关运算:
(1)状态集合I的ε-闭包,表示为ε-closure(I),定义为一状态集,是状态集中的任何状态S经任意条ε弧而能够到达的状态的集合。
(2)状态集合I的a弧转换,表示为move(I,a)定义为状态集合J,其中J是所有那些可从I中的某一状态经过一条a弧而到达的状态的全体。
4.2NFA的确定化
1.子集法
先把DFAM’中的Q’和F’置为空集;
M’的开始状态q0’=[q0],把[q0]置为未标记后加入到Q’中;
如果Q’中存在未标记的状态[q1,q2,…,qi],则对每个a∈∑定义:
δ’([q1,q2,…,qi],a)=[p1,p2,…,pi]当且仅当δ({q1,q2,…,qi},a)={p1,p2,…,pi}。
如果[q1,q2,…,qi]不在Q’中,则把它置为为标记后加入到Q’中;如果p1,p2,…,pi中至少有一个是M的终态,则同时把[p1,p2,…,pi]加入到F’中;然后给Q’中所有的状态都标记为止;
重复执行(3),直到不能向Q’中加入新状态,并且Q’中所有的状态都有标记为止;
重新命名Q’中的状态,最后获得等价的DFAM’。
2.对含ε变迁的NFA的确定化:
置Q’,F’为空集;
令q0’=[ε_CLOSURE({q0})],并把[q0]置为未标记后加入到Q’中;
如果Q’中存在未标记状态[q1,q2,…,qi],则对每个a∈∑定义:
d‘([q1,q2,…qi],a)=[p1,p2,…,pj]当且仅当d({q1,q2,…qi},a)={r1,r2,…,rk},ε_CLOSURE({r1,r2,…,rk})={p1,p2,…,pj}。
如果[p1,p2,…,pj]不在Q’中,则把它置为未标记后加入到Q’中;如果p1,p2,…,pj中至少有一个是M的终态,则同时把[p1,p2,…,pj]加入到F’中;然后给Q’中的状态[q1,q2,…,qi]加上标记;
重复执行
,直到不能向Q’中加入新状态,并且Q’中所有的状态都有标记为止;
重新命名Q’中的状态,然后获得等价的DFAM’
4.3实现方法
(1)有穷自动机的确定化:
在main()函数中通过使用move()和eclouse()和outputfa()函数来实现;
(2)确定的有穷自动机的最小化:
在main()函数中使用chan结构体来实现,在该函数中还调用了outputfa()函数。
4.4设计流程图
1.输入NFA各边信息,通过输入(起点条件[空为*]终点)三项进行输入以#为输入结束;然后再输入各结点中的终结点;输入完成后系统开始执行,先生成NFA状态转换矩阵,重命名,输出DFA状态转换矩阵,最后对DFA状态转换矩阵进行最小化输出。
NFA-DFA处理流程图如下图图4.4-1所示。
图4.4-1NFA-DFA处理流程图
2.NFA转换为DFA的原理及过程通过以下例子说明:
假如输入的NFA如图4.4-2所示:
图4.4-2NFA状态转换图
对于图4.2-2中的状态转换图的状态转换矩阵为:
在图4.2-2中的状态转换图加一个起始状态X和结束状态Y,I为起始节点X经过一个或多个ε边到达的状态集,Ia为{X,1}经过a边到达的结点的ε闭包,Ib经过b边到达的结点的ε闭包;知道Ia和Ib列都在I列中出现为止。
如下表4.2.1所示:
表4.2.1状态转换矩阵
I
Ia
Ib
{X,1}
{2,3,Y}
{2,3,Y}
{2,3,Y}
对表4.2.1的状态转换矩阵进行重新命名,令A={X,1},B={2,3,Y},转换结果如下表4.2.2所示:
表4.2.2重命名的状态转换矩阵
I
Ia
Ib
A
B
B
B
表4.2.2相对应的DFA状态转换图为下图图4.2-2所示:
图4.2-2DFA状态图
结果与分析(可以加页):
1.对于下图1中的NFA:
图1NFA状态图
2.输入图1中NFA的各边信息:
如下截图图2所示:
图2输入各边信息截图
3.对于图1的NFA状态转换矩阵和重命名后的状态矩阵如下截图图3所示:
图3DFA状态矩阵截图
4.将图3中的状态矩阵图最小化,如下截图图4所示:
图4最小化的DFA
5.根据图4最小化的DFA状态转换矩阵,画出DFA状态转换图,如下图图5所示:
图5DFA状态装换图
设计体会与建议:
编译原理是一门重要但难学的课程,因为编译原理是关于编写编译器的技术,编译器的编写一直被认为是十分困难的事情,所以这次选到课程设计的课题就有些无从下手的感觉,感觉任务挺艰巨的。
设计要求从理论上就不太好理解,不像以前的设计编写一个应用程序实现常见的功能,这次的课程设计注重各种算法的实现,比如用子集法实现不确定的有限自动机的确定化、又能够分割法实现确定的有限自动机的最小化。
课题虽然感觉难懂了一些,但我想不管结果如何,只要自己努力,尽力了就行。
小组各成员始终相信“世上无难事,只怕有心人”。
由于课程设计有几周的时间,时间很充裕,所以这在这几周设计中,我们小组团结合作,互帮互助,大家都感觉这几周过的非常充实,尽管累了点。
同时通过这次课程设计我们深深地感觉到团队的力量,深深体会到“团结就是力量”。
当我们开始困惑无从下手时,我们你一句我一句的意见很快让我们找到了前进的方向。
当自己遇到疑难时,和组员一起讨论解决了问题感觉非常欣慰,无助和孤独感也顿时消失。
通过这次课程设计给我另一个很大的体会是:
流程图的重要性。
说实话,每次不管是课程设计报告还是实习报告,我们最害怕做的就是画流程图,所以每次报告中能省则省。
而这次发现根据系统需实现的功能和算法画出流程图后,就很清楚地知道我们的程序需要哪些模块,每个模块需要实现什么功能。
所以在我们以后的学习中我们不能畏惧画流程图,我们要积极锻炼我们画流程图的能力,从而来帮助我们提高编程的能力。
每次的课程设计,和以前的相比,我发现了自己的一次又一次的进步,因为越到最后,越会发现自己的不足,就会想着怎样去改进,怎样去完善自己的系统,所以每学期的课程设计是很必要的,因为我在这些过程中都学到了很多知识。
附源代码:
#include
#include
#defineMAXS100
usingnamespacestd;
stringNODE;//结点集合
stringCHANGE;//终结符集合
intN;//NFA边数
structedge{
stringfirst;
stringchange;
stringlast;
};
structchan{
stringltab;
stringjihe[MAXS];
};
voidkong(inta)
{
inti;
for(i=0;icout<<'';
}
//排序
voidpaixu(string&a)
{
inti,j;
charb;
for(j=0;jfor(i=0;iif(NODE.find(a[i])>NODE.find(a[i+1]))
{
b=a[i];
a[i]=a[i+1];
a[i+1]=b;
}
}
voideclouse(charc,string&he,edgeb[])
{
intk;
for(k=0;k{
if(c==b[k].first[0])
if(b[k].change=="*")
{
if(he.find(b[k].last)>he.length())
he+=b[k].last;
eclouse(b[k].last[0],he,b);
}
}
}
voidmove(chan&he,intm,edgeb[])
{
inti,j,k,l;
k=he.ltab.length();
l=he.jihe[m].length();
for(i=0;ifor(j=0;jif((CHANGE[m]==b[j].change[0])&&(he.ltab[i]==b[j].first[0]))
if(he.jihe[m].find(b[j].last[0])>he.jihe[m].length())
he.jihe[m]+=b[j].last[0];
for(i=0;ifor(j=0;jif((CHANGE[m]==b[j].change[0])&&(he.jihe[m][i]==b[j].first[0]))
if(he.jihe[m].find(b[j].last[0])>he.jihe[m].length())
he.jihe[m]+=b[j].last[0];
}
//输出
voidoutputfa(intlen,inth,chan*t)
{
inti,j,m;
cout<<"I";
for(i=0;icout<<'I'<cout<for(i=0;i{
cout<<''<m=t[i].ltab.length();
for(j=0;j{
kong(8-m);
m=t[i].jihe[j].length();
cout<}
cout<}
}
voidmain()
{
edge*b=newedge[MAXS];
inti,j,k,m,n,h,x,y,len;
boolflag;
stringjh[MAXS],endnode,ednode,sta;
cout<<"请输入NFA各边信息(起点条件[空为*]终点),以#结束:
"<for(i=0;i{
cin>>b[i].first;
if(b[i].first=="#")break;
cin>>b[i].change>>b[i].last;
}
N=i;
for(i=0;i{
if(NODE.find(b[i].first)>NODE.length())
NODE+=b[i].first;
if(NODE.find(b[i].last)>NODE.length())
NODE+=b[i].last;
if((CHANGE.find(b[i].change)>CHANGE.length())&&(b[i].change!
="*"))
CHANGE+=b[i].change;
}
len=CHANGE.length();
cout<<"结点中属于终态的是:
"<cin>>endnode;
for(i=0;iif(NODE.find(endnode[i])>NODE.length())
{
cout<<"所输终态不在集合中,错误!
"<return;
}
chan*t=newchan[MAXS];
t[0].ltab=b[0].first;
h=1;
eclouse(b[0].first[0],t[0].ltab,b);//求e-clouse
for(i=0;i{
for(j=0;jfor(m=0;meclouse(t[i].ltab[j],t[i].jihe[m],b);//求e-clouse
for(k=0;k{
move(t[i],k,b);//求move(I,a)
for(j=0;jeclouse(t[i].jihe[k][j],t[i].jihe[k],b);//求e-clouse
}
for(j=0;j{
paixu(t[i].jihe[j]);//对集合排序以便比较
for(k=0;k{
flag=operator==(t[k].ltab,t[i].jihe[j]);
if(flag)
break;
}
if(!
flag&&t[i].jihe[j].length())
t[h++].ltab=t[i].jihe[j];
}
}
cout<"<outputfa(len,h,t);//输出状态转换矩阵
//状态重新命名
string*d=newstring[h];
NODE.erase();
cout<"<for(i=0;i{
sta=t[i].ltab;
t[i].ltab.erase();
t[i].ltab='A'+i;
NODE+=t[i].ltab;
cout<<'{'<for(j=0;jif(sta.find(endnode[j])d[1]=ednode+=t[i].ltab;
for(k=0;kfor(m=0;mif(sta==t[k].jihe[m])
t[k].jihe[m]=t[i].ltab;
}
for(i=0;iif(ednode.find(NODE[i])>ednode.length())
d[0]+=NODE[i];
endnode=ednode;
cout<"<outputfa(len,h,t);//输出DFA
cout<<"其中终态为:
"<//DFA最小化
m=2;
sta.erase();
flag=0;
for(i=0;i{
for(k=0;k{
y=m;
for(j=0;j{
for(n=0;n{
if(d[n].find(t[NODE.find(d[i][j])].jihe[k]){
if(t[NODE.find(d[i][j])].jihe[k].length()==0)
x=m;
else
x=n;
if(!
sta.length())
{
sta+=x+48;
}
else
if(sta[0]!
=x+48)
{
d[m]+=d[i][j];
flag=1;
d[i].erase(j,1);
j--;
}
break;//跳出n
}
}
}
if(flag)
{
m++;
flag=0;
}
sta.erase();
}
}
cout<";
for(i=0;icout<<"{"<cout<//状态重新命名
chan*md=newchan[m];
NODE.erase();
cout<"<for(i=0;i{
md[i].ltab='A'+i;
NODE+=md[i].ltab;
cout<<"{"<}
for(i=0;ifor(k=0;kfor(j=0;j{
if(d[i][0]==t[j].ltab[0])
{
for(n=0;n{
if(!
t[j].jihe[k].length())
break;
else
if(d[n].find(t[j].jihe[k]){
md[i].jihe[k]=md[n].ltab;
break;
}
}
break;
}
}
ednode.erase();
for(i=0;ifor(j=0;jif(d[i].find(endnode[j])ednode+=md[i].ltab;
endnode=ednode;
cout<"<outputfa(len,m,md);
cout<<"其中终态为:
"<}