计算理论:NFA转DFA的两种方法

时间:2023-12-06 15:30:08

本文将以两种方法实现NFA转DFA,并利用C语言实现。

方法二已利用HNU OJ系统验证,方法一迷之WA,但思路应该是对的,自试方案,测试均通过。

(主要是思路,AC均浮云,大概又有什么奇怪的Case没想到)

==========================================================

下面的描述以机械工业出版社的《计算理论导引》的第三版35页图为例。该NFA如下图。

计算理论:NFA转DFA的两种方法

  • 思路一:穷举组合状态,构造DFA

    该思路接近《计算理论》课本35页思路。

    • 新DFA的状态数

      为了能够保存NFA的各种不确定状态,DFA利用更多的状态来模拟保存。假设NFA有N个状态,那么每一个状态都有到达和没有到达两种可能,也就是新的DFA有2^N个新的状态。例如当我们到达上图中的状态1时,由于存在空漂移,那么我们可能到达了3状态,也就是新DFA状态中的101(5)号状态。
    • 核心思路

      先把这种思路的核心代码放在下面:

      /**
      *Param int CurrState 当前需要查找转移状态的当前状态
      *Param int *NextStateArray 在当前输入情况下的NFA的状态转移表
      *Param int NFA_size NFA的大小
      */
      int find_next(int CurrState,int* NextStateArray,int NFA_size){
      int NextState = 0,i = 0,NFAState = 0;
      for(i = NFA_size-1; i>=0;i--){
      NFAState = CurrState/(1<<i);
      CurrState -= NFAState * (1<<i);
      if(NFAState == 1) NextState = NextState|NextStateArray[i];
      //若CurrState中对应的NFA状态为1,就去NFA转移表中找到该状态的下一个状态
      //并将这些状态添加到DFA的下一个状态中去
      }
      return NextState;
      }

      这段代码展现的是思路一、二均使用的核心。以上文提到过的5号(101)状态来举例。

      currstate = 5(101)的find_next过程

      状态号为5(101)代表着该状态包含了原NFA中的状态3和状态1

      假设当前输入为0,则传到find_next的参数为转移表中0的那一列:0(000) 6(110) 5(101)

      循环中依次访问了101包含的NFA1号状态中的下态(000)和3号状态的下态(101)

      通过位或添加到了DFA当前状态的下一个状态中。

      当遍历完所有currstate包含的NFA状态,返回统计好的下一状态(101)

      find_next函数就是通过这样的方法,对新的DFA中的一个状态在一种输入情况下的状态转移计算出来。获取完整的DFA状态转移,只需要对新的DFA中的所有状态均执行这个过程。(但是这样也就没有考虑一些不可能到达的状态,或可以通过建立后修建得到)

      计算理论:NFA转DFA的两种方法

  • 思路二

    思路二接着刚才的思路一,若我们转成的DFA中有很多很多没有可能到达的状态怎么办?何不逆向考虑,我们从DFA的起始状态开始,推算其有可能达到的DFA状态,判断该状态是否生成。若没有生成,再生成这个DFA新状态。

    下面用伪代码来形式化这一个过程。

    • 核心思路

      function define_DFA_state (int currstate)
      for currstate 中的每一个包含的NFA状态
      currstate 的0输入转移 = 这些NFA状态的0输入转移取位或(思路同find_next)
      currstate 的1输入转移 = 这些NFA状态的1输入转移取位或
      end for
      //这里就完成了currstate的转移定义
      if currstate 的0输入转移未被定义 define_DFA_state 这个状态
      if currstate 的1输入转移未被定义 define_DFA_state 这个状态
      //这样就圆满了,能转移走到的都定义了
      end function
    • 注意

      这种思路带来的后果是DFA并不是按照0,1,2,3这样排序好的状态,生成状态表的时候可能会顺序比较混乱。记得在代码中重新给状态编号,来更清楚生成状态转移表。

      计算理论:NFA转DFA的两种方法