编译原理Java实现——NFA确定化为DFA

不确定的有穷自动机(NFA)
 
是一个五元组 M = (K, ∑, f, S, Z)

其中:K是状态集,∑是输入符号集,f是转化映像,S是初态集,Z是终态集

不确定是说下一个状态可以有多个
 
 

确定的有穷自动机(DFA)
 
是一个五元组 M = (K, ∑, f, S, Z)

其中:K是状态集,∑是输入符号集,f是转化映像,S是唯一初态,Z是终态集

给定当前状态和输入符号,f就可以唯一确定下一个状态  

 

DFA确定化的核心算法: 子集法

通俗的说:首先明确,

  1. NFA中的多个状态,共同组成了DFA中的多个状态
  2. 我们先将NFA的初态集的闭包入队,然后BFS
  3. 每次从queue中取出一个状态集,用字母表的每个字母对其进行一次转换后取闭包(ε-closure(move(I))),如果产生新状态则入队,继续BFS。

其中,较为关键是基于f的一次转化函数move、以及ε-closure闭包函数。代码中专门有注释说明。

 
 

首先定义NFA和DFA的JavaBean类

// NFA类(属性与命名与五元式完全一致)

public class NFA {

    private List<Integer> K;    // 状态集

    private char[] letters;     // 字母表

    private String[][] f;       // 转换函数

    private List<Integer> S;    // 初态集

    private List<Integer> Z;    // 终态集

	// setter和getter这里省略
}
// DFA类(属性与命名与五元式完全一致)

public class DFA {

    private List<Integer> K;    // 状态集

    private char[] letters;     // 字母表

    private String[][] f;       // 转换函数

    private int S;    			// 唯一初态

    private List<Integer> Z;    // 终态集

	// setter和getter这里省略
}

 
 

下面是子集法的核心逻辑,注意move和closure函数尤为关键 >_<

public class NFA2DFA {

    /**
     * definite:【子集法】将NFA确定化为DFA
     * @param nfa 输入nfa
     * @return    返回dfa
     *
     * 伪代码逻辑:
     *  DFA状集合C(注:C的每个成员又都是NFA的状态state的集合)
     *  Queue 临时队列queue;
     *  NFA的初态集S(K0)的闭包入队;
     *  while(队不空):
     *      取出当前状态I;
     *      for 每个输入字母 in letters:
     *             nextI = ε-closure(move(I, letter));
     *             if(C 不包含 nextI) :
     *                  则nextI入队
     *
     *  最终的C即为确定化生成的DFA的状态集
     */
    public DFA definite(NFA nfa){
        List<Integer> K0 = nfa.getS();					// 这几行是把nfa对象的属性先拿下来
        char[] letters = nfa.getLetters();
        String[][] f = nfa.getF();
        List<Integer> Z = nfa.getZ();

        DFA dfa = new DFA();
        List<Integer> K = new ArrayList<>();             // DFA状态集合(已重命名)
        int k = 0;                                       // 用于DFA状态集的重命名
        List<String[]> listF = new ArrayList<>();        // 用于暂存转换函数f
        List<Integer> listZ = new ArrayList<>();         // 用于暂存终态集Z
        StringBuilder sb = new StringBuilder();          // 用于输出整个子集法的过程(debug用)

        Set<List<Integer>> set = new HashSet<>();               // 状态集临时集合
        Queue<List<Integer>> queue = new LinkedList<>();        // 状态集临时队列
        Map<List<Integer>, Integer> map = new HashMap<>();      //「状态集」向「命名」的映射

        List<Integer> closure_K = closure(K0, f);
        K.add(k);
        map.put(closure_K, k++);
        queue.add(closure_K);
        while(!queue.isEmpty()){						// BFS
            List<Integer> I = queue.poll();
            for(char letter : letters){
                List<Integer> nextI = closure(move(I, letter, f), f);
                if(nextI.isEmpty()) continue;
                if(!containsI(set, nextI)){	 
                	// 如果产生了一个新状态,就进行接下来的诸多操作
                	// 给它一个新名字并维护k、确定它是不是终态(包含NFA的终态)、入集合、入队
                    nextI.sort(Comparator.comparing(Integer::intValue));
                    map.put(nextI, k);
                    K.add(k);
                    if(!Collections.disjoint(nextI, Z)){
                        listZ.add(k);
                    }
                    set.add(nextI);
                    queue.add(nextI);
                    k++;
                }
                System.out.print(I);			// 未重命名输出一下(debug用)
                System.out.println(nextI);		// 未重命名输出一下(debug用)
                listF.add(new String[]{String.valueOf(map.get(I)), String.valueOf(letter), String.valueOf(map.get(nextI))});
                sb.append(map.get(I)).append(letter).append(map.get(nextI)).append('\n');
            }
        }
      
        System.out.println(sb.toString());		// 重命名后的状态和转化情况(debug用)

        // 下面是构造出DFA对象,作为返回值
        int len = K.size();
        String[][] f2 = new String[len][len];
        for(String[] tmp : f2){
            Arrays.fill(tmp, "");
        }
        for(String[] arr : listF){
            f2[Integer.parseInt(arr[0])][Integer.parseInt(arr[2])] += arr[1];
        }
        dfa.setK(K);
        dfa.setLetters(letters);
        dfa.setF(f2);
        dfa.setS(0);
        dfa.setZ(listZ);
        return dfa;
    }

    /**
     * ε-closure闭包运算:某个状态集经过任意多个ε,得到当前的真正状态集
     * @param I 当前状态集
     * @return 当前真正的状态集(closureI)
     */
    private List<Integer> closure(List<Integer> I, String[][] f){
        // 经过任意多个ε,因此BFS ! ! !
        List<Integer> closureI  = new ArrayList<>();
        Queue<Integer> queue = new LinkedList<>();
        for(int i : I){
            queue.add(i);
            while (!queue.isEmpty()){
                int n = queue.poll();
                for(int iNext = 0; iNext < f.length; iNext++){
                    for(char c : f[n][iNext].toCharArray()){
                        if(c == 'ε' && !closureI.contains(iNext)){
                            closureI.add(iNext);
                            if(n != iNext){
                                queue.add(iNext);
                            }
                        }
                    }
                }
            }
        }
        return closureI;
    }

    /**
     * move方法:当前状态集通过某个字母可以转化到的下一状态集(一步转换,没有进行ε-闭包运算)
     * @param I 当前的状态集
     * @return 返回下一状态集
     */
    private List<Integer> move(List<Integer> I, char letter, String[][] f){
        List<Integer> nextI = new ArrayList<>();
        for(int i : I){
            for(int iNext = 0; iNext < f.length; iNext++){
                for(char c : f[i][iNext].toCharArray()){
                    if(c == letter && !nextI.contains(iNext)) {
                        nextI.add(iNext);
                    }
                }
            }
        }
        return nextI;
    }



    /**
     * 判断一个Set<List>是否包含某个List
     * @param set
     * @param list
     */
    private boolean containsI(Set<List<Integer>> set, List<Integer> list){
        for(List<Integer> l : set){
            if(listEquals(l, list)){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断两个List集合是否相等(不考虑顺序)
     * @param list1
     * @param list2
     */
    private boolean listEquals(List<Integer> list1, List<Integer> list2){
        list1.sort(Comparator.comparing(Integer::intValue));
        list2.sort(Comparator.comparing(Integer::intValue));
        return list1.toString().equals(list2.toString());
    }

}

 

 

 

 

最后,完整代码项目可以戳这里:CSDN GitHub

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值