提高dpll算法的性能

时间:2012-10-24 21:56:27

标签: c++ algorithm recursion sat-solvers

我正在使用wikipedia中所述的C ++实现 DPLL 算法:

function DPLL(Φ)
   if Φ is a consistent set of literals
       then return true;
   if Φ contains an empty clause
       then return false;
   for every unit clause l in Φ
      Φ ← unit-propagate(l, Φ);
   for every literal l that occurs pure in Φ
      Φ ← pure-literal-assign(l, Φ);
   l ← choose-literal(Φ);
   return DPLL(Φ ∧ l) or DPLL(Φ ∧ not(l));

但表现糟糕。在这一步:

return DPLL(Φ ∧ l) or DPLL(Φ ∧ not(l));

目前我正在尝试避免创建Φ的副本,而是将lnot(l)添加到Φ的唯一副本中,并在/ if时将其删除DPLL()返回false。这似乎打破了算法给出错误结果(UNSATISFIABLE,即使该集合为SATISFIABLE)。

有关如何在此步骤中避免使用显式副本的任何建议吗?

1 个答案:

答案 0 :(得分:1)

一种不那么天真的DPLL方法可以避免通过记录变量赋值和单位传播和纯文字赋值步骤中对子句所做的更改来复制公式,然后在生成空子句时撤消更改(回溯) 。因此,如果将变量 x 指定为true,则会将包含 x 的正文字的所有子句标记为非活动状态(之后忽略它们,因为它们已满足)并删除 -x 来自包含它的所有子句。记录哪些子句中包含 -x ,以便稍后可以回溯。同样记录您标记为非活动的条款。

另一种方法是跟踪每个未满足条款中未分配变量的数量。记录数字何时减少,以便您可以稍后回溯。如果计数达到1则进行单位传播,如果数字达到0则回溯,并且所有文字均为假。

我上面写的“不太天真”,因为还有更好的方法。现代DPLL类型SAT求解器使用称为“两个监视文字”的惰性子句更新方案,其优点是不需要从子句中删除文字,因此在找到错误的赋值时不需要恢复它们。变量赋值仍然必须被记录和回溯,但不必更新与子句相关的结构使得两个观察文字比任何其他已知的SAT求解器回溯方案更快。你无疑会在课堂上了解这一点。