编译原理——NFA转DFA实验全过程+实验代码

  • 实验目的

设计并实现将NFA确定化为DFA的子集构造算法,从而更好地理解有限自动机之间地等价性,掌握词法分析器自动产生器的构造技术。该算法也是构造LR分析器的基础。

 

  • 实验要求

设计并实现计算状态集合I的ε闭包的算法ε_Closure(I)和转化函数Move(I,a),并在此基础上实现子集构造算法Subset_Construction。利用该从NFA到DFA的转换程序Subset_Construction,任意输入一个NFA N=(S,Σ,δ,s0,F),输出一个接收同一语言的DFA M=(S’,Σ,δ’,s0’,F’)。

 

  • 实验内容

①实验原理

  1. 令I是NFA N的状态集S的一个子集,I的ε闭包的ε_Closure(I)构造规则如下:
  1. 若s∈I,则s∈ε_Closure(I);
  2. 若s∈ε_Closure(I)且δ(s,ε)=s’而s’∉ε_Closure(I),则s’ ∈ε_Closure(I)

根据上面的规则,下面给出了计算I的ε闭包的算法ε_Closure(I)。

SET S;

SET ε_Closure(input)

SET *input;

{

S=input;/*初始化*/

push(); /*把输入状态集中的全部状态压入栈中*/

while(栈非空){

Nfa_state i;

pop();/*把栈顶元素弹出并送入i*/

if(存在δ(i,ε)=j){

把i加到S中;

把j压入栈中;

}

}

return S;/*返回ε_Closure(input)集合*/

}

完成上述算法的设计。

  1. 令I是NFA N状态集S的一个子集,a∈Σ,转换函数Move(I,a)定义为:

 Move(I,a)=ε_Closure(I)

其中,J={s’|s∈I且δ(s,ε)=s’}

转换函数Move(I,a)的设计通过调用ε_Closure(input)实现,完成该函数设计。

  1. 从NFA N构造一个与其等价的DFA M的子集构造算法,就是要为DFA M构 造状态转换表Trans,表中的每个状态是NFA N状态的集合,DFA M将“并行” 地模拟NFA N面对输入符号串所有可能地移动。下面给出了子集构造算法 Subset_Construction的框架,请完成其设计过程。

有关数据结构:

States[] 是一个M的数组,每个状态有两个域,set域存放的状态集合,flg域为一标识。

Trans[] 是M的转移矩阵(输入字母表Σ元素个数×最大状态数),Trans[i][a]=下一状态。

i  代表M的当前状态号

a  代表输入符号,a∈Σ

Nstates[] M的下一新状态号

S 定义M的一个状态的N的状态集

初始化:

States[0].set=ε_Closure({N的初态})

States[0].flg=FALSE

Nstates=1

i=0

S=Ф

Trans初始化为无状态“-”

while(States[i]的flg为FALSE){

States[i].flg=TRUE;

for(每个输入符号a∈Σ){

S=ε_Closure(Move(States[i].set,a));

if(S非空)

if(States中没有set域等于S的状态){

States[Nstates].set=S;

States[Nstates].flg=FALSE;

Trans[i][a]=Nstates++;

}

else

Trans[i][a]=States中一个set域为S的下标;

}

}

此算法的输出M主要由Trans矩阵描述,其中省略每个状态是否为终态的描 述,应加以完善。

(4)测试用例

对下图所示的NFA N用子集构造算法Subset_Construction确定化。

NFA N的初态为12,DFA M的初态为ε_Closure({12})。

整个转换过程可用下表来概括。

DFA M的状态转换图如下。

 

②state.txt(存放结点状态)

③trans.txt(存放结点间的状态转换路径)

④运行的实验结果

  • 实验代码
#include <iostream>
#include <fstream>
#include <set>
#include <queue>
#include <vector>
#include<string.h>
#define MAX 10
using namespace std;

//相关全局变量
std::set<int> NFASet;     //NFA状态集
set<char> charSet;        //接受字符集
vector<set<int>> DFASet;   //DFA状态集
int nfaNum, charsNum, dfaNum;
char** trans = nullptr;   //NFA状态转换表,由文件动态读入创建
char DFAGraph[MAX][MAX];  //DFA状态转换表
bool isEnd[MAX];   //DFA终止状态判断
//函数申明
void InitNfa(const char* path, int& startState, int& endState);  //nfa初始化
void InitTransMatrix(const char* path);  //状态转换表初始化
template <typename T>
void printTransMatrix(T trans, int num);    //格式化打印
void printSet(set<int> I);
set<int> getEClosure(set<int> I);   //空弧闭包
set<int> getMoveTo(set<int> I, char ac);   //ac字符闭包
int isSubset(set<int> I);  //判断I是否是DFASet的子集
void subsetConstruction(int startState, int endState);  //子集构造法确定DFA



int main() {

    set<int> DFAEndStates;
    int startState, endState;
    const char* statePath = "state.txt";  //结点
    const char* transPath = "trans.txt";  //状态转换

    //nfa状态集初始化
    InitNfa(statePath, startState, endState);
    nfaNum = NFASet.size();

    cout << "确定NFA初始值:" << endl;
    std::cout << "  NFA的状态总数:" << nfaNum << std::endl;
    cout << "  开始状态:" << startState << endl;
    cout<< "  结束状态:" << endState << endl;
   
    cout << endl;
    //NFA状态转换表
    cout << "NFA状态转换表:" << endl;
    InitTransMatrix(transPath);
    charsNum = charSet.size();  //接受字符集大小
    printTransMatrix(trans, nfaNum);
    cout << "状态转移的字符条件(除#):" << ' ';
    for (const char& ac : charSet) { cout << ac << ' '; }
    cout << endl;
 
    //子集构造法确定DFA
    subsetConstruction(startState, endState);

    return 0;
}


void InitNfa(const char* path, int& startState, int& endState) {
    ifstream infile(path); // 打开文件
    if (!infile) {
        cerr << "文件打开错误!" << endl;
        return;
    }

    int state, flag;
    char ch;  // 分割符:
    while (infile >> state >> ch >> flag) { // 逐行读取数据
        NFASet.insert(state);
        if (flag == 0) { startState = state; }
        else if (flag == 2) { endState = state; };
    }
    infile.close();
}

void InitTransMatrix(const char* path) {
    ifstream infile(path); // 打开文件
    if (!infile) {
        cerr << "文件打开错误!" << endl;
        return;
    }

    trans = new char* [nfaNum];
    for (int i = 0; i < nfaNum; ++i) {
        trans[i] = new char[nfaNum + 1]; // +1 为了存储空字符'\0'
        for (int j = 0; j < nfaNum; ++j) {
            trans[i][j] = ' '; // 初始化为空格或其它占位符
        }
        trans[i][nfaNum] = '\0'; // 确保每行都是以空字符结尾的字符串
    }

    int startState, endState;
    char acceptChar;
    while (infile >> startState >> acceptChar >> endState) { // 逐行读取数据
        trans[startState][endState] = acceptChar;
        if (acceptChar != '#' and acceptChar) {
            //当acceptChar非空时加入字符集
            charSet.insert(acceptChar);
        }
    }
    infile.close();
}
template <typename T>
void printTransMatrix(T trans, int num) {
    // 打印表头
    printf("  "); // 空格用于对齐
    for (int j = 0; j < num; j++) {
        printf("%2d ", j); // 打印列号
    }
    printf("\n");
    // 打印分隔线
    for (int i = 0; i < num * 3.5; i++) {
        printf("-");
    }
    printf("\n");

    // 打印二维数组
    for (int i = 0; i < num; i++) {
        printf("%2d ", i); // 打印行号,加1是因为通常人们习惯从1开始计数
        for (int j = 0; j < num; j++) {
            printf("%c |", trans[i][j]); // 打印字符
        }
        printf("\n");
    }

    for (int i = 0; i < num * 3.5; i++) {
        printf("-");
    }
    printf("\n");

}
template <typename T>
void printTransMatrixx(T trans, int num) {
    // 打印表头
    printf("  "); // 空格用于对齐
    for (int j = 0; j < num; j++) {

        printf("%4d ", j); // 打印列号
    }
    printf("\n");
    // 打印分隔线
    for (int i = 0; i < num * 6; i++) {
        printf("-");
    }
    printf("\n");

    // 打印二维数组
    for (int i = 0; i < num; i++) {
        printf("%d ", i); // 打印行号,加1是因为通常人们习惯从1开始计数
        for (int j = 0; j < num; j++) {
           
            
            if (i == 1 && j == 2) { printf("     |"); }
            else if (i == 1 && j == 3) { printf("   %c|",trans[i][j]); }
            else if (i == 2 && j == 1) { printf("     |"); }
            else if (i == 2 && j == 2) { printf("     |"); }
            else if (i == 2 && j == 3) { printf("    |"); }
            else if (i == 2 && j == 4) { printf("   %c|", trans[i][j]); }
            else if (i == 3 && j == 1) { printf("     |"); }
            else if (i == 3 && j == 2) { printf("     |"); }
            else if (i == 3 && j == 3) { printf("    |"); }
            else if (i == 3 && j == 5) { printf("   %c|",trans[i][j]); }
            else if (i == 4 && j == 1) { printf("     |"); }
            else if (i == 4 && j == 2) { printf("     |"); }
            else if (i == 4 && j == 3) { printf("    |"); }
            else if (i == 4 && j == 4) { printf("   %c|",trans[i][j]); }
            else if (i == 4 && j == 5) { printf("    |"); }
            else if (i == 5 && j == 1) { printf("     |"); }
            else if (i == 5 && j == 5) { printf("    |"); }
            else{
            printf("    %c|", trans[i][j]);} // 打印字符
        }
        printf("\n");
    }

    for (int i = 0; i < num * 6; i++) {
        printf("-");
    }
    printf("\n");

}


void printSet(set<int> I) {
    for (const int& s : I) {
        cout << s << " ";
    }
    cout << endl;
}

/*计算I的eclosure集合*/
set<int> getEClosure(set<int> I) {                            //计算get_eclosure(I)集,方法广度优先搜索
    //拓扑排序算法
    std::vector<bool> visited(nfaNum, false);
   
    
    

    set<int> newSet;
    queue<int> Q;                                               //辅助队列

    for (set<int>::iterator it = I.begin(); it != I.end(); it++)       //复制当前集合并入队
    {
        Q.push(*it);
        newSet.insert(*it);
    }

    //类似图的广度遍历
    while (!Q.empty())                                           //队列每次弹出首位,并以首位进行扩展
    {
        int q = Q.front();
        Q.pop();                //扩展以后弹出
        for (int i = 0; i < nfaNum; i++)
        {
            if (trans[q][i] == '#' && !visited[i])
            {   //存在接受空弧"#",从q->i
                newSet.insert(i);
                Q.push(i);
                visited[i] = true;                               //如果找到了,那么就加入新的set,并标记已访问
            }
        }
    }
    return newSet;
}

//计算字符ac的闭包
set<int> getMoveTo(set<int> I, char ac) {
    set<int> newSet;
    for (set<int>::iterator it = I.begin(); it != I.end(); it++)
    {
        int curState = *it;
        for (int i = 0; i < nfaNum; i++)
        {
            if (trans[curState][i] == ac)
                newSet.insert(i);
        }
    }
    return newSet;
}

//子集判断
int isSubset(set<int> st) {
    for (int i = 0; i < DFASet.size(); ++i) {
        if (DFASet[i] == st) {
            return i;  //是子集则返回集合在vector中的下标
        }
    }
    return -1;
}
//子集构造法确定NFA
void subsetConstruction(int startState, int endState) {
    memset(isEnd, false, MAX);
    set<int> cur;  //当前状态集合
    //将初始结点加入cur集合中,并求出其空弧闭包,得出初始状态集合
    cur.insert(startState);
    cur = getEClosure(cur);
    DFASet.push_back(cur);  //加入DFA状态集合
 
    cout << "NFA确定化的开始状态集合:";
    printSet(cur);
    cout << endl;

    //循环构造并不断更新DFASet
    for (int i = 0; i < DFASet.size(); ++i) {
        cur = DFASet[i];
        for (const char& ch : charSet) {
            set<int> newSet;
            newSet = getEClosure(getMoveTo(cur, ch));  //寻找ch字符的闭包的空弧闭包
            if (!newSet.empty()) {
                //输出闭包
                cout << "对应DFA状态的重新编号的闭包:" << i << "\t{";
                for (const int& st : DFASet[i]) {
                    cout << st << ",";
                }
                cout << "} | " << ch << " closure: ";
                printSet(newSet);
            }
            if (!newSet.empty()) {
                int idx = isSubset(newSet);  //该闭包是否包含于DFASet
                if (idx == -1) {
                    //如果结果集合不是DFASet的子集且非空,则加入
                    DFASet.push_back(newSet);
                    int len = DFASet.size();
                    DFAGraph[i][len - 1] = ch;  //从DFA的第i状态输入字符ch转换成第len-1状态,即刚插入进去的状态

                    if (newSet.count(endState) > 0) {
                        //终止结点属于该集合->其是DFA终止状态
                        isEnd[len - 1] = true;
                    }

                }
                else {
                    //是子集,从i->idx
                    DFAGraph[i][idx] = ch;
                }
            }
        }
        cout << "是不是结束状态(0:不是;1:是):" << isEnd[i] << endl;
     
        cout << endl;
    }
    cout << endl;
    dfaNum = DFASet.size();
    cout << "DFA的状态转移矩阵:" << endl;
    printTransMatrixx(DFAGraph, dfaNum);

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值