Loading

DFA极小化算法,矩阵扫描实现,编译原理

极小化算法

看了一下网上的方法基本上是使用“集合的分割”实现的极小化,刚好我们老师使用的是矩阵扫描,因此自己干脆使用java来实现。我直接封装了一个dfa的类,目前只写了“矩阵扫描”的极小化,有时间的话自己也会把“集合分割”的算法加上。下面就拿网上找到的例子做一个使用矩阵扫描的基本步骤。

矩阵扫描

关键步骤:

  1. 去除无用状态
  2. 合并等价状态

例子:

dfa01

第一步:

画一个矩阵,可以是下面这样的。因为(A,C)和(C,A)一样,也就是说这是一个对角阵,所以只需要使用下三角(上三角)即可:

第二步

去除对角线,以-1作为去除的标记

第三步

终态和非终态是不能合并的,因此需要把终态和非终态去掉。由DFA知道,非终态集为{A,B,C,D},终态集为{E},在下三角中把(E,A),(E,B),(E,C),(E,D)去掉。这一步我使用1做为标记,因为在java里面Int型默认为0,所以这样做可以避免初始化的时候赋值。
注意:在程序中除了对角线使用-1,其余我都使用1和0作为标记,1表示不可合并,0为可合并。
下面是去掉后的结果:

第四步

开始做下三角的扫描,只需要扫描空白(程序中标记为0)的地方即可,当然在程序中循环扫描的时候,还是需要遍历下三角的。
第一遍:

  1. B,A
    f(B,a) = B f(A,a) = B (解释:这既是跳转函数,B输入a跳到B状态,A输入a也跳转到B状态。因为B和B是同一个状态,所以保留B,A)
    f(B,b) = D f(A,b) = C (D,C状态未知,保留)
  2. C,A
    f(C,a) = B f(A,a) = B
    f(C,b) = C f(A,b) = C
  3. C,B
    f(C,a) = B f(B,a) = B
    f(C,b) = C f(B,b) = D
  4. D,A
    f(D,a) = B f(A,a) = B
    f(D,b) = E f(A,b) = C (解释:(E,C)已经被标记上1,所以(D,A)也要被标记为1)
  5. D,B
    f(D,a) = B f(B,a) = B
    f(D,b) = E f(B,b) = D (同(D,A))
  6. D,C
    f(D,a) = B f(C,a) = B
    f(D,b) = E f(C,b) = C
    第一遍扫描后的结果如下:

    注意:因为在上一次的基础上,下三角的状态已经发生了变化,所以需要进行第二次扫描。如果第二次又发生变化,那么就需要第三次扫描。直至状态不在改变,这时候就是最小化的最终结果。
    第二遍:
  7. B,A
    f(B,a) = B f(A,a) = B
    f(B,b) = D f(A,b) = C (因为(D,C) == 1,所以(B,A) = 1)
  8. C,A
    f(C,a) = B f(A,a) = B
    f(C,b) = C f(A,b) = C
  9. C,B
    f(C,a) = B f(B,a) = B
    f(C,b) = C f(B,b) = D

本次扫描结果:

下三角状态发生改变,进行第三遍扫描:
第三遍:

  1. C,A
    f(C,a) = B f(A,a) = B
    f(C,b) = C f(A,b) = C

这一遍后,下三角的状态没有发生改变,不需要在进行第四遍扫描。这说明扫描已经结束了,而最终结果便是上一次扫描的结果:

从图中可以看出,(C,A)处留空,说明A,C是可合并的状态,其他状态不可以合并
**结果:**可合并的状态为{A,C}

需要说明的是,扫描的步骤中,我每一步的跳转都写出来了,这只是为了更好的理解和阐述,同时说明了在程序中实现的过程。但是在实际的手动扫描时,很多重复的步骤是可以省略的。

JAVA程序实现的例子

因为把dfa封装成了类,所以在get和set方法上花了很大的篇幅。因此下面只粘出“矩阵扫描”的方法。源代码的下载链接,博文最后有附上。

	/**
	 * 采用矩阵扫描法
	 * @return 成功:下三角矩阵,失败:null
	 */
	public int[][] matrixScan() {
		//定义一个下三角矩阵,存储结果
		//可合并为0
		//否则为1
		int[][] matrix = new int[getSSet().size()][];
		for (int i = 0; i < matrix.length; i++) {
			matrix[i] = new int[i + 1];
			//去掉对角线
			matrix[i][i] = 1;
		}

		//去掉终态
		for (int i = 1; i < matrix.length; i++) {
			// 判断i是否是终态
			if (!isSSetItem(i)) {
				for (int j = 0; j < i; j++) {
					//判断j是否是终态
					//不是终态则matrix[i][j] = 1;
					if (isSSetItem(j))
						matrix[i][j] = 1;
				}
			}else{
				for (int j = 0;j < i;j++){
					if (!isSSetItem(j))
						matrix[i][j] = 1;
				}
			}
		}

		// 最后的扫描
		// 局部内部类
		class ScanMatrix {
			void scan() {
				// 记录扫描状态
				boolean status = true;
				while (status) {
					status = false;
					for (int i = 1; i < getSSet().size(); i++) {
						for (int j = 0; j < i; j++) {
							//判断是否已经为1
							if (matrix[i][j] == 0) {
								for (int ci = 0; ci < getCSet().size(); ci++) {
									int max = getFunItem(i, ci);
									int min = getFunItem(j, ci);
									if (max < min) {
										int temp = max;
										max = min;
										min = temp;
									}
									// 注意max和min不应该相等
									// 既不能是对角线上的1
									if (matrix[max][min] == 1 && max != min) {
										matrix[i][j] = 1;
										status = true;
										break;
									}
								}
							}
						}
					}
				}
			}
		}
		new ScanMatrix().scan();
		return matrix;
	}

程序运行例子得到的结果

测试数据:

A:0,B:0,C:0,D:0,E:1
a,b
B,C,B,D,B,C,B,E,B,C

结果:

源代码下载:
点击跳转

运行结果肯定是和分析的结果一样,但是在编写的时候难免会有疏漏的地方,敬请指正!

原文地址:https://www.startfu.cn/2019/dfa%e6%9e%81%e5%b0%8f%e5%8c%96%e7%ae%97%e6%b3%95%ef%bc%8c%e7%9f%a9%e9%98%b5%e6%89%ab%e6%8f%8f%e5%ae%9e%e7%8e%b0%ef%bc%8c%e7%bc%96%e8%af%91%e5%8e%9f%e7%90%86.html

posted @ 2019-05-24 15:49  CPoet  阅读(540)  评论(0)    收藏  举报