SWI-Prolog CLPFD

时间:2015-10-17 08:19:04

标签: prolog clpfd

我是限制性编程的新手。我有一个问题,CLPFD没有像我期望的那样减少域名。这可能很简单。

 [A,B] ins 1..5,A*B#=5.

我希望它将A和B的域减少到

1\/5

但它只是给出了

A in 1..5,
A*B#=5,
B in 1..5.

任何建议都将不胜感激。

2 个答案:

答案 0 :(得分:4)

虽然这个答案是根据中的量身定制的,但这个构思/方法是可移植的。

:- use_module(library(clpfd)).

以下是我们如何在开始完全枚举之前减少域大小

A

我们使用SWI-Prolog clpfd manual page上定义的语法A_

_

示例查询:

shave_zs(Zs) :-
   maplist(flag_zs_shave_z(F,Zs), Zs),
   once((var(F) ; ground(Zs) ; shave_zs(Zs))).

flag_zs_shave_z(Flag, Zs, Z) :-
   (  fd_size(Z, sup)
   -> true                                    % never shave the infinite
   ;  fd_dom(Z, Z_dom),
      phrase(dom_integers_(Z_dom), Z_vals),
      maplist(flag_zs_z_val(Flag,Zs,Z), Z_vals)
   ).

flag_zs_z_val(Flag, Zs, Z, Z_val) :-
   (  \+ call_with_inference_limit((Z #= Z_val,labeling([],Zs)), 1000, _)
   -> Z #\= Z_val,
      Flag = true
   ;  true
   ).

答案 1 :(得分:2)

你是对的,1\/5在这种情况下是最佳修剪。

但是,出于效率原因,CLP(FD)系统通常仅为算术约束维护所谓的边界一致性,并且通常不会从域中删除内部元素,即使其中一些元素无法参与在解决方案中。

边界一致性,在有限的情况下,意味着存在变量假设域的下边界和上边界的解。在这种情况下,有A=1A=5的解决方案。

请注意,这是这个具体案例中唯一的解决方案,但总的来说,在类似的大型实例中也有内部点的解决方案,例如:

?- [A,B] ins 1..10, A*B#=10, label([A,B]).
A = 1,
B = 10 ;
A = 2,
B = 5 ;
A = 5,
B = 2 ;
A = 10,
B = 1.

但好消息是,此类解决方案的数量只会以域的大小呈对数增长:

?- length(_, Exp), N #= 2^Exp, [A,B] ins 1..N,A*B#=N,
   findall(., label([A,B]), Ls), length(Ls, L),
   writeln(Exp-L), false.
0-1
1-2
2-3
3-4
4-5
5-6
6-7
7-8
etc.

这与X mod 2 #= 0之类的其他情况形成对比,其中解决方案的数量线性X的域大小增长(因此以十进制表示的长度指数,因此明确修剪域是不可行的。

因此,作为一种可行的解决方法,您可以使用label/1来获取具体的解决方案,然后使用in/2约束将操作数限制为其具体允许的域:

:- use_module(library(clpfd)).

stricter_domains(Vs) :-
        findall(Vs, label(Vs), Sols0),
        transpose(Sols0, Sols),
        maplist(list_to_domain, Sols, Ds),
        maplist(in, Vs, Ds).

list_to_domain([L|Ls], Dom) :- foldl(dom_disj, Ls, L, Dom).

dom_disj(D0, I, D0\/I).

你的例子:

?- [A,B] ins 1..5, A*B#=5, stricter_domains([A,B]).
A in 1\/5,
A*B#=5,
B in 1\/5.