编译原理(二) NFA的确定化及DFA的最小化的算法及C++实现

最后更新于:2022-04-01 14:15:13

## 1. NFA的确定化 ### 1.1. 明确NFA的定义 一个非确定的有穷自动机(NFA)M是一个五元式: - M=(S,∑,δ,S0,F) - S是一个有限集,它额每个元素称为一个状态。 - ∑是一个有穷字母表,它的每个元素称为一个输入字符 - δ是一个从S×∑∗至S子集额单值映射。即:δ:S×∑∗→2⋅S - S0⊆S,是一个非空的初态集 - F⊂ S , 是一个终态集(可空) ### 1.2. 定义运算 定义对状态集合I的几个有关运算: - 状态集合I的ε-闭包,表示为ε-closure(I),定义为一状态集,是状态集I中的任何状态s经任意条ε弧而能到达的状态的集合。状态集合I的任何状态s都属于ε-closure(I)。 - 状态集合I的a弧转换,表示为move(I,a)定义为状态集合J,其中J是所有那些可从I的某一状态经过一条a弧而到达的状态的全体。 定义Ia = ε-closure(move(I,a)) ### 1.3. 算法描述 - 每次从队头取出一个集合,**(开始队列内只有初态集合I的ε-闭包(I) )**,然后得到它对于任意一个字符a的Ia=ε−closure(move(I,a)) - 然后如果当前状态之前没有出现过,那么当前状态作为一个新的状态I,放入队列。 - 一直做如上操作,直到队列为空 ### 1.4. 代码如下: ~~~ #include <iostream> #include <cstdio> #include <algorithm> #include <fstream> #include <queue> #include <vector> #include <string> #include <cstring> #include <set> #define MAX 500 #define M 1000007 using namespace std; struct Set { set<int> elements; int state; }I[MAX]; vector<int> e[MAX]; vector<int> edge[MAX]; vector<int> val1[MAX]; vector<int> val2[MAX]; vector<int> hash[M]; vector<int> types; int vis[MAX][MAX]; int cntp,cnts,start,finish,used[MAX]; void _clear( ); void clear( ); void _add( int u , int v , int x ); void add( int u , int v , int x ); void dfs ( set<int>& elements , int u , int d , int flag ); void ensure(); void minimize(); int get_hash( set<int>& item ); //#define DEBUG int main ( ) { int m; puts("给出NFA的边的条数:"); while ( ~scanf ( "%d" , &m ) ) { _clear(); clear(); puts("给出NFA的始态和终态:"); scanf ( "%d%d" , &start , &finish ); puts("给出所有的边,每行一条:"); for ( int i = 0 ; i < m ; i++ ) { int u,v,x; scanf ( "%d%d%d" , &u , &v , &x ); _add ( u , v , x ); } #ifdef DEBUG set<int> temp; int num[]={2,3,6,7,8}; //for ( int i = 2 ; i < 6 ; i++ ) // dfs ( temp , i , 0 , 2 ); for ( int i = 0 ; i < 5 ; i++ ) dfs ( temp , num[i] , 0 , 1 ); set<int>::iterator it = temp.begin(); for ( ; it != temp.end() ; it++ ) printf ( "%d " , *it); puts(""); #else ensure(); puts("计算结果如下:"); printf ( "%d\n" , cnts ); for ( int i = 0 ; i < cnts ; i++ ) for ( int j = 0 ; j < edge[i].size() ; j++ ) printf ( "edges : %d %d %d\n" , i , edge[i][j] , val2[i][j] ); puts("\n给出DFA的边的条数:"); #endif } } void _clear() { for ( int i = 0 ; i < MAX ; i++ ) { e[i].clear(); val1[i].clear(); } types.clear(); memset ( used , 0 , sizeof ( used ) ); } void clear() { for ( int i = 0 ; i < MAX ; i++ ) { edge[i].clear(); val2[i].clear(); } } void _add ( int u , int v , int x ) { e[u].push_back ( v ); val1[u].push_back ( x ); if ( !used[x] ) { types.push_back ( x ); used[x] = types.size(); } } int get_hash ( set<int>& item ) { int sum = 0; set<int>::iterator it = item.begin(),it1,it2; for ( ; it!= item.end() ; it++ ) { sum += (*it)*(*it)%M; sum %= M; } for ( int i = 0 ; i < hash[sum].size() ; i++ ) { int x = hash[sum][i]; set<int>& temp = I[x].elements; it = temp.begin(); bool flag = true; if ( temp.size() != item.size() ) continue; for ( it1 = temp.begin() , it2 = item.begin(); it2!= item.end() ; it1++,it2++ ) if ( *it1 != *it2 ) { flag = false; break; } if ( flag ) return x; } return -1; } int make_hash ( set<int>& item ) { int sum = 0; set<int>::iterator it = item.begin(); for ( ; it!= item.end() ; it++ ) { sum += (*it)*(*it)%M; sum %= M; } hash[sum].push_back ( cnts ); return cnts++; } void add ( int u , int v , int x ) { edge[u].push_back ( v ); val2[u].push_back ( x ); } void dfs ( set<int>& elements , int u , int d , int flag ) { if ( vis[u][d] ) return; vis[u][d] = 1; if ( d == 1 ) elements.insert ( u ); if ( d == 2 ) return; int len = e[u].size(); for ( int i = 0 ; i < len ; i++ ) { int v = e[u][i],dd; int x = val1[u][i]; if ( x == flag ) dd = d+1; else if ( x == 0 ) dd = d; else continue; dfs ( elements , v , dd , flag ); } } void ensure ( ) { I[0].elements.insert(start); cnts = 1; for ( int i = 0 ; i < cnts ; i++ ) { set<int>& cur = I[i].elements; for ( int j = 0 ; j < types.size() ; j++ ) { if ( types[j] == 0 ) continue; memset ( vis , 0 , sizeof ( vis ) ); set<int>& temp = I[cnts].elements; temp.clear(); set<int>::iterator it = cur.begin(); for ( ; it != cur.end() ; it++ ) dfs ( temp , *it , 0 , types[j] ); if ( temp.empty() ) continue; int x = get_hash ( temp ); if ( x == -1 ) x = make_hash ( temp ); add ( i , x , types[j] ); } set<int>::iterator it = cur.begin(); printf ( "I%d :" , i ); for ( ; it!= cur.end() ; it++ ) printf ( "%d " , *it ); puts (""); } } ~~~ ## 2. DFA的最小化 ### 2.1. 明确DFA的定义 一个确定的有穷自动机(DFA)M是一个五元式: - M=(S, ∑, δ, s0, F)其中 - S是一个有限集,它的每个元素称为一个状态。 - ∑是一个有穷字母表,它的每个元素称为一个输入字符 -δ是一个从S×∑至S的单值映射。δ(s,a)=s’意味着:当现行状态-为s、输入字符为a时,将转换到下一个状态s’。我们称s’为s的一个后继状态。 - s0∈S,是唯一的初态。 - F ⊂ S,是一个终态集(可空) ### 2.2 算法描述 - DFA M =(K,∑,f, k0,, kt),最小状态DFA M’ - 1.构造状态的初始划分∏0:终态kt 和非终态K- kt两组 - 2.对∏施用传播性原则 构造新划分∏new - 3.如∏new=∏,则令∏new=∏并继续步骤4,否则∏:=∏new重复2 - 4.为∏final中的每一组选一代表,这些代表构成M’的状态。若k是一代表且f(k,a)=t,令r是t组的代表,则M’中有一转换f’(k,a)=r M’ 的开始状态是含有K0的那组的代表 M’的终态是含有Kt的那组的代表 - 5.去掉M’中的死状态. ### 2.3 代码实现 ~~~ #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <string> #include <vector> #include <map> #include <queue> #include <fstream> #define MAX 507 using namespace std; struct Node { int x; int type; Node(){} Node( int a , int b ) :x(a),type(b){} bool operator < ( const Node& a ) const { return type < a.type; } }; int mp[MAX][MAX]; vector<int> _edge[MAX]; vector<int> _val[MAX]; vector<int> edge[MAX]; vector<int> val[MAX]; vector<int> point; vector<pair<int,int> > ans1; vector<int> ans2; vector<Node> col; int fa[MAX]; int type[MAX]; int state[MAX]; int used[MAX]; int kinds; int groups_num; int n; int m; void init (); int _find( int x ); void _union ( int x , int y ); void _add ( int u , int v , int x ); void add ( int u , int v , int x ); void minimize ( ); void make_csv(); int main ( ) { init ( ); scanf ( "%d%d" , &n , &m ); for ( int i = 1 ; i <= n ; i++ ) scanf ( "%d" , &type[i] ); for ( int i = 0 ; i < m ; i++ ) { int u,v,x; scanf ( "%d%d%d" , &u , &v , &x ); _add ( u ,v , x ); } minimize(); //最小化后的点的保留结果 puts ("points : " ); printf ( "%d\n" , (int)point.size() ); for ( int i = 0 ; i < point.size(); i++ ) printf ( "%d " , point[i] ); puts(""); //最小化后的边的保留结果 puts ( "edge: "); m = ans1.size(); for ( int i = 0 ; i < ans1.size() ; i++ ) printf ( "%d %d %d\n" , ans1[i].first , ans1[i].second , ans2[i] ); puts(""); make_csv(); } void init ( ) { for ( int i = 0 ; i < MAX ; i++ ) { edge[i].clear(); _edge[i].clear(); } point.clear(); memset ( type , 255 , sizeof ( type ) ); memset ( used , 0 , sizeof ( used ) ); kinds = 0; groups_num = 2; col.clear(); for ( int i = 0 ; i < MAX ; i++ ) fa[i] = i; } void _add ( int u , int v , int x ) { _edge[u].push_back ( v ); _val[u].push_back ( x ); if ( used[x] ) return ; used[x] = x; kinds++; } void add ( int u , int v , int x ) { edge[u].push_back ( v ); val[u].push_back ( x ); } int _find ( int x ) { return x == fa[x]? x: fa[x] = _find ( fa[x] ); } void _union ( int x , int y ) { x = _find ( x ); y = _find ( y ); fa[x] = y; } void minimize ( ) { queue<vector<Node> > q[2]; col.clear(); for ( int i = 1 ; i <= n ; i++ ) { if ( type[i] == 0 ) col.push_back ( Node( i , 0 ) ); } q[1].push ( col ); col.clear(); for ( int i = 1 ; i <= n ;i++ ) { if ( type[i] == 1 ) col.push_back ( Node ( i , 1 ) ); } q[1].push( col ); col.clear(); for ( int i = 1 ; i <= kinds ; i++ ) { int cur = i%2; int next = (i+1)%2; while ( !q[next].empty() ) q[next].pop(); while ( !q[cur].empty() ) { vector<Node> front = q[cur].front(); q[cur].pop(); for ( int j = 0 ; j < front.size() ; j++ ) { Node& temp = front[j]; int u = temp.x; for ( int k = 0 ; k < _edge[u].size() ; k++ ) { int v = _edge[u][k]; int x = _val[u][k]; if ( x != i ) continue; temp.type = type[v]; } } sort ( front.begin() , front.end() ); if ( front[0].type == front[front.size()-1].type ) q[next].push ( front ); else { col.clear(); col.push_back ( front[0] ); for ( int j = 1 ; j < front.size() ; j++ ) { if ( front[j].type != front[j-1].type ) { q[cur].push ( col ); col.clear(); groups_num++; } type[front[j].x] = groups_num; col.push_back ( front[j] ); } q[cur].push( col ); } } } //#define DEBUG #ifdef DEBUG int id = (kinds+1)%2; int num = 1; while ( !q[id].empty() ) { vector<Node> temp = q[id].front(); q[id].pop(); printf ( "%d : ", num++ ); for ( int i = 0 ; i < temp.size() ; i++ ) printf ( "%d " , temp[i].x ); puts(""); } #else int id = (kinds+1)%2; while ( !q[id].empty() ) { vector<Node> temp = q[id].front(); q[id].pop(); for ( int i = 1 ; i < temp.size() ; i++ ) _union ( temp[i].x , temp[i-1].x); } memset ( used , 0 , sizeof ( used ) ); memset ( mp , 0 , sizeof ( mp ) ); for ( int i = 1 ; i <= n ;i++ ) { int x = _find(i); if ( used[x] ) continue; used[x] = 1; point.push_back ( x ); } ans1.clear(); for ( int i = 1 ; i <= n ; i++ ) for ( int j = 0 ; j < _edge[i].size() ; j++ ) { int v = _edge[i][j]; int x = _val[i][j]; int a = _find(i); int b = _find(v); if ( mp[a][b] ) continue; mp[a][b] = 1; add ( a , b , x ); ans1.push_back ( make_pair ( a , b ) ); ans2.push_back ( x ); } #endif } void make_csv ( ) { freopen ( "g1.csv" , "w" , stdout ); printf ( "%d,%d, \n" , n , m ); for ( int i = 0 ; i < point.size() ; i++ ) if ( i == 0 ) printf ( "%d" , point[i] ); else printf ( ",%d" , point[i] ); puts(""); for ( int i = 0 ; i < m ; i++ ) printf ( "%d,%d,%d\n" , ans1[i].first , ans1[i].second , ans2[i] ); fclose(stdout); } ~~~
';