我在理解DPLL算法时遇到一些问题,我想知道是否有人可以向我解释,因为我认为我的理解是不正确的。
我理解它的方式是,我采用一些文字,如果某些每个子句都为真,则模型为真,但如果某个子句为假,则模型为假。
我通过查找一个单元子句来递归检查模型,如果有的话我设置了该单元子句的值使其成为真,然后更新模型。删除现在为true的所有子句并删除现在为false的所有文字。
当没有单位子句时,我选择了任何其他文字并为该文字指定值使其成立并使其成为假,然后再次删除现在为真的所有子句以及现在为假的所有文字。
答案 0 :(得分:8)
DPLL要求以析取正常形式陈述问题,即作为一组条款,每一条都必须得到满足。
每个子句都是一组文字{l1, l2, ..., ln}
,表示这些文字的分离(即,要满足的子句必须至少有一个文字。)
每个文字l
断言某些变量为真(x
)或者为假(~x
)。
如果子句中的任何文字都为真,那么该子句就满足了。
如果一个子句中的所有文字都是假的,那么该条款是不可满足的,因此问题是不可满足的。
解决方案是为变量分配true / false值,以便满足每个子句。 DPLL算法是对此类解决方案的优化搜索。
DPLL本质上是一种深度优先搜索,它在三种策略之间交替。在搜索的任何阶段都有部分分配(即,对某些变量子集赋值)和一组未决条款(即那些尚未满足的条款)。
(1)第一个策略是Pure Literal Elimination:如果未分配的变量x
仅在未确定的子句中以正面形式出现(即,文字~x
不会出现在任何地方)然后我们只需将x = true
添加到我们的作业中,并满足包含文字x
的所有子句(类似地,如果x
仅以其否定形式出现~x
,我们就可以只需将x = false
添加到我们的作业中。
(2)第二种策略是单位传播:如果一个未定的条款中的一个文字都是假的,那么剩下的一个必须是真的。如果剩余的字面值为x
,我们会将x = true
添加到我们的作业中;如果剩余的字面值为~x
,我们会在分配中添加x = false
。这项任务可以为单位传播带来更多机会。
(3)第三种策略是简单地选择一个未分配的变量x
并对搜索进行分支:一方尝试x = true
,另一方尝试x = false
。
如果在任何时候我们最终得到一个不满意的条款,那么我们已经走到了死胡同并且不得不回溯。
有各种巧妙的进一步优化,但这是几乎所有SAT求解器的核心。
希望这有帮助。
答案 1 :(得分:1)
Davis-Putnam-Logemann-Loveland(DPLL)算法是一种基于回溯的搜索算法,用于确定命题逻辑公式在可称正态形式(也称为可满足性问题或SAT)中的可满足性。
任何布尔公式都可以用合取范式(CNF)表示,这意味着条款的连接,即(...)^(...)^(...)
其中一个子句是布尔变量的分离,即(A v B v C'v D)
在CNF中表示的布尔公式的示例是
(A v B v C)^(C'v D)^(D'v A)
并解决SAT问题意味着找到满足它的公式中变量的值组合,如A = 1,B = 0,C = 0,D = 0
这是NP-Complete问题。实际上这是第一个被Stepehn Cook和Leonid Levin证明是NP-Complete的问题
特定类型的SAT问题是3-SAT,它是一个SAT,其中所有子句都有三个变量。
DPLL算法是解决SAT问题(实际上取决于输入的硬度)的方法,递归地创建了一个潜在解决方案树
假设您想解决像这样的3-SAT问题
(A v B v C)^(C'v D v B)^(B v A'v C)^(C'v A'v B')
如果我们枚举像A = 1 B = 2 C = 3 D = 4这样的变量,并且对于A'= -1这样的否定变量是负数,那么可以用Python编写相同的公式
[[1,2,3],[ - 3,4,2],[2,-1,3],[ - 3,-1,-2]
现在想象创建一个树,其中每个节点都包含一个部分解决方案。在我们的例子中,我们还描述了解决方案满足的子句的向量
根节点是[-1,-1,-1,-1],这意味着尚未为变量分配0和1
每次迭代:
我们采用第一个不满意的条款
如果没有更多未分配的变量我们可以用来满足该子句,那么在搜索树的这个分支中就没有有效的解决方案,算法将返回无
否则我们取第一个未赋值变量并设置它使它满足该子句并从步骤1开始递归。如果算法的内部调用返回None,我们翻转变量的值,使其不满足该子句并设置下一个未分配的变量以满足该子句。如果已经尝试了所有三个变量或者该子句没有更多未分配的变量,则意味着该分支中没有有效的解决方案,算法将返回无
请参阅以下示例:
从根节点我们选择第一个子句的第一个变量(A)(A v B v C)并设置它使得它满足条件,然后A = 1(搜索树的第二个节点)
继续第二个子句,我们选择第一个未分配的变量(C)并设置它,使其满足C = 0的子句(左边的第三个节点)
我们对第四个子句(B v A'v C)做同样的事情,并将B设置为1
我们尝试对最后一个句子做同样的事情,我们意识到我们不再有未分配的变量,并且该子句总是假的。然后我们必须回溯到搜索树中的先前位置。我们更改了我们分配给B的值并将B设置为0.然后我们查找另一个可以满足第三个子句的未分配值,但是没有。然后我们必须再次回溯到第二个节点
在那里,我们必须翻转第一个变量(C)的赋值,以便它不满足该子句并设置下一个未赋值变量(D)以满足它(即C = 1和D = 1 )。这也满足包含C的第三个子句。
满足(C'v A'v B')的最后一个子句有一个未赋值的变量B,然后可以设置为0以满足该子句。
在此链接http://lowcoupling.com/post/72424308422/a-simple-3-sat-solver-using-dpll中,您还可以找到实现它的python代码