使用' mod'时必须具体化。运营商与'或'?

时间:2016-05-24 15:17:24

标签: prolog clpfd

我使用CLP(FD)和SWI-Prolog编写了一个CSP程序。

我认为我需要改善我的约束条件。我使用mod运算符时写 与我的谓词中的#\/一起。

一个简短的例子:

:- use_module(library(clpfd)).

constr(X,Y,Z) :-
   X in {1,2,3,4,5,6,7},
   Y in {3,5,7},
   Z in {1,2},
   ((X #= 3)) #==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)),
   ((Z #= 1)) #<==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)).

如果我致电constr(3,Y,Z).,我会Z #= 1Z #= 2。 这是因为仍然需要评估一些中间变量(相对于mod表达式)。

当然理想的是只获得Z #= 1

怎么可以这样做?

我知道如果我写了

((X #= 3)) #==> ((Z #= 1)),
((Z #= 1)) #<==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)).

一切都按预期工作。

但这是必须的吗?我的意思是,每次我在约束中使用此模式时,是否必须创建一个reification变量:

(A mod n1 #= 0) #\/ (B mod n2 #= 0) #\/ ... #\/ (Z mod n26 #= 0)

提前感谢您的想法。

3 个答案:

答案 0 :(得分:5)

这是一个非常好的观察和问题!首先,请注意,这绝不是mod/2特有的。例如:

?- B #<==> X #= Y+Z, X #= Y+Z.
B in 0..1,
X#=_G1122#<==>B,
Y+Z#=X,
Y+Z#=_G1122.

相反,如果我们以声明的方式将其写为:

?- B #<==> X #= A, A #= Y + Z, X #= A.

然后我们完全按预期得到:

A = X,
B = 1,
Y+Z#=X.

这里发生了什么?在我所知道的所有系统中,通常使用CLP(FD)表达式的分解,遗憾的是删除了以后无法恢复的重要信息。在第一个示例中,检测到约束X #= Y+Z 需要,即必须包含

另一方面,正确检测与非复合参数的单一相等性,如第二个例子所示。

所以是的,一般情况下,您需要以这种方式重写约束,以便最佳地检测蕴涵。

潜在的问题当然是CLP(FD)系统是否可以帮助您检测此类情况并自动执行重写。同样在这种情况下,答案是,至少对于某些情况。但是,CLP(FD)系统通常只被告知特定序列中的各个约束,并且重新创建和分析所有已发布约束的全局概览以合并或组合先前分解的约束通常是不值得的。

答案 1 :(得分:3)

使用(半官方)onActivityResult谓词,您可以一举最小化某些域名。在你的情况下:

contracting/1

现在增加一个目标:

| ?- constr(3,Y,Z).
clpz:(Z#=1#<==>_A),
clpz:(_B#=0#<==>_C),
clpz:(_D#=0#<==>_E),
clpz:(_F#=0#<==>_G),
clpz:(_H#=0#<==>_I),
clpz:(_C#\/_E#<==>1),
clpz:(_G#\/_I#<==>_A),
clpz:(Y mod 3#=_B),
clpz:(Y mod 3#=_F),
clpz:(Y mod 7#=_D),
clpz:(Y mod 7#=_H),
clpz:(Y in 3\/5\/7),
clpz:(Z in 1..2),
clpz:(_C in 0..1),
clpz:(_B in 0..2),
clpz:(_E in 0..1),
clpz:(_D in 0..6),
clpz:(_A in 0..1),
clpz:(_G in 0..1),
clpz:(_F in 0..2),
clpz:(_I in 0..1),
clpz:(_H in 0..6) ? ;
no

换句话说,谓词| ?- constr(3,Y,Z), clpz:contracting([Z]). Z = 1, clpz:(_A#=0#<==>_B), clpz:(_C#=0#<==>_D), clpz:(_E#=0#<==>_F), clpz:(_G#=0#<==>_H), clpz:(_B#\/_D#<==>1), clpz:(_F#\/_H#<==>1), clpz:(Y mod 3#=_A), clpz:(Y mod 3#=_E), clpz:(Y mod 7#=_C), clpz:(Y mod 7#=_G), clpz:(Y in 3\/5\/7), clpz:(_B in 0..1), clpz:(_A in 0..2), clpz:(_D in 0..1), clpz:(_C in 0..6), clpz:(_F in 0..1), clpz:(_E in 0..2), clpz:(_H in 0..1), clpz:(_G in 0..6) ? ; no 的更一致的版本是:

constr/3

上面我使用了constr_better(X, Y, Z) :- constr(X, Y, Z), clpz:contracting([Z]). 的SICStus,这是SWI的library(clpz)的继承者library(clpfd)

答案 2 :(得分:1)

在尝试了很多事情之后,我最终得出了这些结论,告诉我我是不是错了(对不起,我是初学者)。

让我们考虑一下这个样本:

:- use_module(library(clpfd)).

constr(X,Y,Z) :-
   X in {1,2,3,4,5,6,7},
   Y in {3,5,7,21,42},
   Z in {1,2},
   (X #= 3) #==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)),
   (Z #= 1) #<==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)).

constr_better(X,Y,Z) :- constr(X,Y,Z), clpfd:contracting([X,Y,Z]).

res(X,L) :- setof(X, indomain(X), L).

constrChoice(X,Y,Z,XOut,YOut,ZOut) :-
   constr(X,Y,Z),
   res(X,XOut),res(Y,YOut),res(Z,ZOut).

constrChoiceBetter(X,Y,Z,XOut,YOut,ZOut) :-
   constr_better(X,Y,Z),
   res(X,XOut),res(Y,YOut),res(Z,ZOut).
  1. constr(3,Y,Z)提供Z in 1..2 ,但 constrChoice(3,Y,Z,Xout,Yout,Zout)提供Zout=[1],因此无需使用contracting/1因为使用setof/3indomain/1一起完成工作。无需重写prolog谓词。

  2. 现在如果我有AND #/\而不是OR #\/,则constr(3,Y,Z)constrChoice(3,Y,Z,Xout,Yout,Zout)constrChoiceBetter(3,Y,Z,Xout,Yout,Zout)调用都不会我有效地认为Y是21或42,但Z被告知是1或2。 有效:直接写Y mod 21 #= 0,然后也不需要使用contracting/1

  3. 感谢您的评论。