计算 CSP 中的解决方案

Count solutions in a CSP

我正在使用序言并且我有这个代码:

:- use_module(library(clpb)).

fun(A, B, C, D, E) :-
  sat(A + B + C + D),
  sat(E),
  labeling([A, B, C, D, E]).

如果我想计算所有的解决方案,我该怎么做?我读过 clpb 中使用的 sat_count(+Expr, -Count) 但我无法在没有错误的情况下实现它

蛮力方法

计算解决方案数量的直接方法是生成它们,然后查看有多少。

例如,对于您发布的程序,我们可以按如下方式使用 findall/3 来获得答案:

?- findall(., fun(A, B, C, D, E), Ls),
   length(Ls, L).
Ls = ['.', '.', '.', '.', '.', '.', '.', '.', '.'|...],
L = 15.

当然,这种扩展性相当差,并且在更复杂的情况下很快变得不可行。尽管如此,在这个例子中,这个策略就足够了。

选择:sat_count/2

对于 CLP(B),我们有 sat_count/2 你已经发现了。

sat_count/2 的主要优点是我们可以计算解决方案的数量 而无需枚举它们 。当解决方案非常多时,这当然非常方便。

使用 sat_count/2 的技巧是 避免 labeling/1,并以仅 的方式编写您的核心关系发布约束.

例如:

fun(A, B, C, D, E) :-
   sat(A + B + C + D),
   sat(E).

这有几个优点。首先,我们现在可以查询:

?- fun(A, B, C, D, E).
E = 1,
sat(A=\= ... # ... # B#B*C#B*C*D#B*D#C#C*D#D).

这个答案表明 E 必然是 1!如果我们使用 labeling/1,这会更难看到。

此外,在发布相关约束后,我们可以使用 sat_count/2,方法是提供一个必须计算为真值的 CLP(B) 表达式作为第一个参数。

在我们的例子中,我们不想对解决方案施加进一步的限制,因此我们将 重言式 指定为第一个参数,其中包含我们感兴趣的变量。A合适的成语是 +[1|Vs].

所以,我们可以使用:

?- fun(A, B, C, D, E),
   sat_count(+[1,A,B,C,D,E], Count).
E = 1,
Count = 15,
sat(A=\= ... # ... # B#B*C#B*C*D#B*D#C#C*D#D).

总结

在这种特定情况下,解决方案的数量很少,两种方法之间几乎没有区别。

不过,在编写约束逻辑程序时,我想强调一个重要的通用原则:

It is good practice to separate the core relation from the actual search for solutions (labeling/1 and similar predicates, both built-in and manually written).

如果您遵守此原则,则可以单独研究约束的终止和传播。

此外,这允许您在不重新编译程序的情况下尝试不同的搜索策略!