Prolog:如何使用prolog的clpfd库列出X和Y可能使用的不同值?

时间:2017-10-16 00:18:44

标签: prolog

 " first == null ? false : second == null ? false : first < second "

这是我尝试这样做的。目标是将其输入SWI-Prolog,以便输出结果。

test(X, Y) :- 
    X ins 1..3,
    Y ins 1..3,
    X #\= Y.

......等等。

我实际上是尝试使用prolog来解决8-queens问题,并且到目前为止还有这个问题。

?- test(X, Y).

X = 1

Y = 2 ;

X = 2,

Y = 1;

X = 3,

Y = 1 ; 

但是我一直收到这个错误:eight_queens(Qs, L) :- Qs = [ [X1,Y1], [X2, Y2], [X3, Y3], [X4, Y4], [X5, Y5], [X6, Y6], [X7, Y7], [X8, Y8], [X9, Y9] ], Qs ins 1..9, X1 #\= X2, X1 #\= X3, ... etc. 对于测试函数和8_queens问题。

2 个答案:

答案 0 :(得分:3)

ins用于列表,in用于单个变量,所以在您的示例中:

test(X, Y) :- 
    X ins 1..3,
    Y ins 1..3,
    X #\= Y.

X,Y被假定为列表。这不会产生语法错误,但在尝试使用X运行它时会产生错误,Y不是列表。

同样在使用in Low..High时,并不意味着变量只是X=<HighX>=Low的int。为了将约束设为整数,请使用label/1

:- use_module(library(clpfd)).

%using in/
    test(X,Y):- X in 1..3,Y in 1..3,label([X,Y]), X#\=Y.

%2nd way using ins
    test(X,Y):- [X,Y] ins 1..3, label([X,Y]), X#\=Y.

示例:

?- test(X,Y).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
false.

答案 1 :(得分:3)

除了@coder发布的/ 2和ins / 2的观察,解决你迫在眉睫的问题之外,我还要添加以下几点,在使用CLP(FD)时要记住:

1。始终将标签作为最后一个目标

首先让我们在@ coder的帖子中观察使用ins 标记为 2nd way的变体的答案,但没有目标标签/ 1:

test(X, Y) :- 
    [X,Y] ins 1..3,
    X #\= Y.

?- test(X,Y).
X in 1..3,          % residual goal
X#\=Y,              % residual goal
Y in 1..3.          % residual goal

由于查询没有唯一的答案,Prolog以剩余目标回答(有关详细信息,请参阅section A.8.8 of the CLP(FD) manual)。这些剩余目标是正在传播的约束,并且随着每个附加(非冗余)约束,域变窄。如果这不会导致上述示例中的唯一解决方案,则可以通过标记约束变量(例如使用label / 1)来获取具体值。该观察结果表明使用标签作为最后目标:

?- test(X,Y), label([X,Y]).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2.

这显然与@coders版本的结果相同,但由于约束而在标记时不考虑三对(X,Y)=(1,1)∨(2,2)∨(3,3)在目标X#\=Y之前发布label([X,Y])。在@ coder的版本中,它是另一种方式:label([X,Y])提供所有三对作为可能的解决方案,最后一个目标X#\=Y随后消除它们。要看到这一点,只需将最后一个目标作为注释并查询谓词:

test(X,Y):- [X,Y] ins 1..3, label([X,Y]). %, X#\=Y.

?- test(X,Y).
X = Y, Y = 1 ;      % <- (1,1)
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = Y, Y = 2 ;      % <- (2,2)
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
X = Y, Y = 3.       % <- (3,3)

在这个例子中,差异是微不足道的,所以@coder的版本没有任何问题。但总的来说,如果贴标后发布的约束排除了很多候选人,这可能会产生很大的差异。因此,始终将标签作为最后一个目标是一种好习惯。

2。将标签与实际关系分开

根据之前的观察结果,将谓词划分为发布所有约束和标签的核心关系是恰当的。将重构的谓词test / 2视为模板:

test(X,Y) :-
   test_(X,Y,L),     % the core relation
   label(L).         % labeling

test_(X,Y,L) :-
   L=[X,Y],          % variables to be labeled in a flat list
   L ins 1..3,
   X#\=Y.

谓词test_ / 3通过发布所有必要的约束来描述实际关系,并且列表作为包含要标记的所有变量的附加参数。获取后者可能并不简单,具体取决于您的参数所带来的数据结构(例如,将列表列表作为参数,您希望将其转换为用于标记的平面列表)。所以谓词test / 2只调用test_ / 3,然后是标记目标。通过这种方式,您可以获得清晰易读的分离。

3。尝试不同的标签策略

目标label(L)是进行标记的最简单方法。它相当于labeling([],L)。标签/ 2的第一个参数是一个选项列表,可以让您对搜索过程进行一些控制,例如: labeling([ff],L)标记最左边的变量,然后是最小的域,以便尽早发现不可行性。根据您尝试解决的问题,不同的标记策略可能会导致结果更快或更慢。有关可用的标签策略和更多示例,请参阅documentation of labeling/2