计算 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).
如果您遵守此原则,则可以单独研究约束的终止和传播。
此外,这允许您在不重新编译程序的情况下尝试不同的搜索策略!
我正在使用序言并且我有这个代码:
:- 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).
如果您遵守此原则,则可以单独研究约束的终止和传播。
此外,这允许您在不重新编译程序的情况下尝试不同的搜索策略!