2-CNF 问题解决方案

本篇文章主要是根据上届一个东大学长谢文艳写的一篇《给定2CNF可满足性问题》自己重新总结了一下,然后把所有的代码都调试了一遍,不过有一点要注意的是,文章中的方法能够判断问题是有具有解,能给出该问题的一种可行解,但并不能给出所有的解。

我自己总结为两个部分,图论问题和算法的详解。

一、将问题转换为图论问题 

首先,将2CNF的问题转换为图论问题。

下面的规则说明了如何把一个2CNF转化为一个有向图G=

u    2CNF的每个变量及这个变量的非都作为图G的顶点。显然若这个2CNF3个变量,那么将有6个顶点。

 

u   

 

第一步首先为那些有到自己的非的有向路径的变量赋值,即如果图G中有从x到的有向路径,那么将变量x赋值为false。如果图G中有从到x的有向路径,则将变量x赋值为true。将在这个步骤中被赋了值的变量称为一级变量。

如下所示:

 

 

 

 

第二步依次从与每一个一级变量对应的节点出发(如果一级变量x被赋值为true,则从与x对应的节点出发;如果x被赋值为false, 则从与对应的节点出发),将所有能到达的、还没有被赋值的节点赋值为true(如果该节点对应于y,则将y赋值为true;如果该节点对应于,则将y赋值为false)。将在这个步骤中被赋了值的变量称为二级变量。

如下所示:

 

第三步:依次检查还没有被赋值的变量,如s,如果与s对应的顶点出度不为0(有指向其它节点的有向边),则将s赋值为true,然后从与s对应的顶点出发,将所有能到达的、还没有被赋值的节点赋值为true;如果与s对应的顶点出度为0,则将s赋值为false,从与s对应的顶点出发,将所有能到达的、还没有被赋值的节点赋值为true。将在这个步骤中被赋了值的变量称为三级变量。

如下所示:

    

 

 

   

二、算法的详解

 

 

1.        先构图,并将图表示为邻接矩阵。思想主要是将每一个字句用邻接矩阵edges表示,如字句xUy,输入的时候表示为:1 1然后,将该字句表示 在数据结构中表示为edges[n+1][2]=1以及edges[2][n+1]=1

2.        并找出一级变量。主要思想是调用isSatisfiable函数,利用广度优先从顶点i到顶点n+i看看是否能走通,同时,也判断从顶点n+i到顶点i,如果都走通了,说明误解,否则判定这n个点后,不存在两个条件都满足的情况,则表示有解了。同时,在判断的过程中,找出一级变量。

 Boolean isSatisfiable() { for (int i = 0; i < varCount; i++) { boolean iToNegi = hasPathTo(i, varCount + i); boolean negiToi = hasPathTo(varCount + i, i); if (iToNegi && negiToi) { return false; } if (iToNegi) { // there is a path from i to negative i,in this case, we can // only assign false to variable i // use 4 to indicate that i is LEVEL 1 variable and its value is // false assignments[i] = 4; determinedCount++; // It also means y and negative y can't both appear in the // reachable vertices of negative i. } else if (negiToi) { // there is a path from negative i to i,in this case, we can // only assign // true to variable i // use 5 to indicate that i is LEVEL 1 variable and its value is // true assignments[i] = 5; determinedCount++; // It also means y and negative y can't both appear in the // reachable vertices of i. } } return true; }

3.        找出二级变量和三级变量。从每一个一级变量出发,根据原则,利用广度优先的算法,把所有能达到的变量变为true,这些变量就是二级变量了。然后再获取三级变量,通过一个循环,将没有访问过的变量,利用的原则来将所有的所能达到的置为true

 public int[] getAssignments() { if (determinedCount == varCount) { // all the variables' value have been pre-determined in // isSatisfiable() function. normalizeValues(); return assignments; } // to determine the values of LEVEL 2 variables. for (int i = 0; i < varCount; i++) { if (assignments[i] == 4) { // start from vertex -i. // use 2 (3) to indicate the variables is in LEVEL 2 and // its value is false (true) boolean finished = toDetermineOtherVar(varCount + i, 2, 3); if (finished) { normalizeValues(); return assignments; } } else if (assignments[i] == 5) { // start from vertex i. boolean finished = toDetermineOtherVar(i, 2, 3); if (finished) { normalizeValues(); return assignments; } } }

4.        自此,算法结束,输出所有变量的值。(注意:该算法只能获得一组可行解,但不能获得所有的解)

 

 

 

如果输入:  

格式如下:

4 3//第一个参数是行数 第二个是变量个数

//记下来是字句输入 表示为-1 2

 

-1 2

-2 3

1 -3

3 2

得出结果:

有可行解

1:1

2:1

3:1

 

 

又例如输入:

 

 

 

 

 

得出结果:

 

1:1 //X1:1
2:0 //x2:0
3:0 //x3:0
4:1 //x4:1
5:0 //x5:0

 

 

点击这里下载完整代码

若有问题欢迎发邮件给我:[email protected]

 

你可能感兴趣的:(2-CNF 问题解决方案)