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 完全问题。实际上这是 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 的变量分配任何值
每次迭代:
然后我们取第一个不满足的子句
1234563
1234563子句并设置下一个未分配的变量以满足该子句。如果所有三个变量都已尝试过,或者该子句没有更多未分配的变量,则意味着该分支中没有有效的解决方案,算法将返回 None
请看下面的例子:
我们从根节点选择第一个子句 (A v B v C) 的第一个变量 (A) 并将其设置为满足子句然后 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代码