如何以约束满足的方式模拟爱因斯坦的谜语(Prolog)

时间:2012-11-25 02:38:14

标签: prolog clpfd zebra-puzzle

我的IA任务是解决爱因斯坦问题。

我必须在Prolog中使用CSP模型解决它。我没有给出模型,只有问题和一些输入数据。我的解决方案必须是一般的解决方案,我的意思是,对于某些输入数据,我必须提供解决方案。问题的维度是N,例如N可能是5(我们有5个房子),但它可以变化。

我在Internet上找到的许多解决方案都将约束直接放在代码中,但我需要使用输入数据生成它们。必须使用MAC(维持弧一致性)算法解决该问题。

我已经阅读了很多关于它的内容(爱因斯坦的谜语)。为了实现这个问题,我需要一个问题的表示。

问题是,我不知道究竟如何在Prolog中表示问题(我知道基本的Prolog,没有使用过额外的库,我们不允许使用clpfd库 - prolog clp求解器)。

我知道我应该从输入中创建约束(14条线索)+表示来自同一组的所有变量(例如国籍)的约束应该是不同的,我可以实现我的谓词:

my_all_different(like all_different/1 offered by clpfd).

例如:

Attributes = ['Color', 'Nationality', 'Drink', 'Smoke', 'Pet'].

Values = [['Blue', 'Green', 'Ivory', 'Red', 'Yellow'],
['Englishman', 'Japanese', 'Norwegian', 'Spaniard', 'Ukrainian'],
['Coffee', 'Milk', 'Orange juice', 'Tea', 'Water'],
['Chesterfield', 'Kools', 'Lucky Strike', 'Old Gold', 'Parliament'],
['Dog', 'Fox', 'Horse', 'Snails', 'Zebra']
]).

Statements = 'The Englishman lives in the red house',
'The Spaniard owns the dog',
'Coffee is drunk in the green house',
'The Ukrainian drinks tea',
'The green house is immediately to the right of the ivory house',
'The Old Gold smoker owns snails',
'Kools are smoked in the yellow house',
'Milk is drunk in the middle house',
'The Norwegian lives in the first house',
'The man who smokes Chesterfield lives in the house next to the man with the fox',
'Kools are smoked in the house next to the house where the horse is kept',
'The Lucky Strike smoker drinks orange juice',
'The Japanese smokes Parliament',
'The Norwegian lives next to the blue house'
]).

Question = 'Who owns a zebra'?

现在,我设法解析了这个输入并获得了一个列表列表:

R = [[red,englishman]
[spaniard,dog]
[green,coffee]
[ukrainian,tea]
[green,ivory,right]
[old,snails]
[yellow,kools]
[milk,middle]
[norwegian,first]
[chesterfield,fox,next]
[kools,horse,next]
[orange,lucky]
[japanese,parliament]
[blue,norwegian,next]].

现在我想我需要使用这个生成的信息来构造一些约束,从我所看到的,使用二进制约束(我认为表示为谓词)是一个好主意,但我也有一些一元约束,所以我应该如何表示约束以包含所有这些?

另一个问题是:如何表示变量(我将拥有计算数据),这样我就不需要搜索和修改列表了(因为在prolog中你不能像命令式语言那样修改列表)

所以我想使用一个变量列表,其中每个变量/元素由一个3元组表示:(var, domain, attrV),其中var包含变量的当前值,domain是一个列表说:[ 1,2,3,4,...,N]和attrV是相应属性(例如红色)的一个值(N)。一个要素是:(C, [1, 2, 3, 4, 5], red)

其他问题:如何在prolog中实现MAC算法(使用AC-3算法),因为我将拥有一个元组队列,如果不满足约束,则会修改此队列,这意味着修改变量列表,再次如何修改Prolog中的列表。

任何帮助将不胜感激!


我尝试使用上面给出的链接中的CSP解算器来解决问题的特定版本,但我仍然无法找到解决方案,我想获得解决方案,因为这样,我会知道如何正确表示一般版本的约束。

添加了代码:

% Computational Intelligence: a logical approach.
% Prolog Code.
% A CSP solver using arc consistency (Figure 4.8)
% Copyright (c) 1998, Poole, Mackworth, Goebel and Oxford University Press.

% csp(Domains, Relations) means that each variable has
% an instantiation to one of the values in its Domain
% such that all the Relations are satisfied.
% Domains represented as list of
% [dom(V,[c1,...,cn]),...]
% Relations represented as [rel([X,Y],r(X,Y)),...]
%  for some r
csp(Doms,Relns) :-
   write('CSP Level'), nl,
   ac(Doms,Relns).

% ac(Dom,Relns) is true if the domain constrants
% specified in Dom and the binary relations
% constraints specified in Relns are satisfied.
ac(Doms,Relns) :-
   make_arcs(Relns,A),
   consistent(Doms,[],A,A),
   write('Final Doms '), write(Doms), nl, %test
   write('Final Arcs '), write(A), nl. %test

% make_arcs(Relns, Arcs) makes arcs Arcs corresponding to
% relations Relns. There are acrs for each ordering of
% variables in a relations.
make_arcs([],[]).
make_arcs([rel([X,Y],R)|O],
          [rel([X,Y],R),rel([Y,X],R)|OA]) :-
   make_arcs(O,OA).

% consistent(Doms,CA,TDA,A) is true if
% Doms is a set of domains
% CA is a set of consistent arcs,
% TDA is a list of arcs to do
% A is a list of all arcs
consistent(Doms,CA,TDA,A) :-
   consider(Doms,RedDoms,CA,TDA),
   write('Consistent Doms '), write(RedDoms), nl, %test
   solutions(RedDoms,A),
   write('Consistent Doms '), write(RedDoms), nl, %test
   write('Consistent Arcs '), write(A), nl. %test

% consider(D0,D1,CA,TDA)
% D0 is the set of inital domains
% D1 is the set of reduced domains
% CA = consistent arcs,
% TDA = to do arcs
consider(D,D,_,[]).
consider(D0,D3,CA,[rel([X,Y],R)|TDA]) :-
   choose(dom(XV,DX),D0,D1),X==XV,
  % write('D0 '), write(D0),
  % write('D1 '), write(D1), nl,
   choose(dom(YV,DY),D1,_),Y==YV, !,
   prune(X,DX,Y,DY,R,NDX),
   ( NDX = DX
   ->
     consider(D0,D3,[rel([X,Y],R)|CA],TDA)
   ; acc_todo(X,Y,CA,CA1,TDA,TDA1),
     consider([dom(X,NDX)|D1],D3,
              [rel([X,Y],R)|CA1],TDA1)).

% prune(X,DX,Y,DY,R,NDX)
% variable X had domain DX
% variable Y has domain DY
% R is a relation on X and Y
% NDX = {X in DX | exists Y such that R(X,Y) is true}
prune(_,[],_,_,_,[]).
prune(X,[V|XD],Y,YD,R,XD1):-
   \+ (X=V,member(Y,YD),R),!,
   prune(X,XD,Y,YD,R,XD1).
prune(X,[V|XD],Y,YD,R,[V|XD1]):-
   prune(X,XD,Y,YD,R,XD1).

% acc_todo(X,Y,CA,CA1,TDA,TDA1)
% given variables X and Y,
% updates consistent arcs from CA to CA1 and
% to do arcs from TDA to TDA1
acc_todo(_,_,[],[],TDA,TDA).
acc_todo(X,Y,[rel([U,V],R)|CA0],
         [rel([U,V],R)|CA1],TDA0,TDA1) :-
   ( X \== V
   ; X == V,
     Y == U),
   acc_todo(X,Y,CA0,CA1,TDA0,TDA1).
acc_todo(X,Y,[rel([U,V],R)|CA0],
         CA1,TDA0,[rel([U,V],R)|TDA1]) :-
   X == V,
   Y \== U,
   acc_todo(X,Y,CA0,CA1,TDA0,TDA1).

% solutions(Doms,Arcs) given a reduced set of
% domains, Dome, and arcs Arcs, solves the CSP.
solutions(Doms,_) :-
   solve_singletons(Doms),
   write('Single Doms '), write(Doms), nl. %test
solutions(Doms,A) :-
   my_select(dom(X,[XV1,XV2|XVs]),Doms,ODoms),
   split([XV1,XV2|XVs],DX1,DX2),
   acc_todo(X,_,A,CA,[],TDA),
   ( consistent([dom(X,DX1)|ODoms],CA,TDA,A)
   ; consistent([dom(X,DX2)|ODoms],CA,TDA,A)).

% solve_singletons(Doms) is true if Doms is a
% set of singletom domains, with the variables
% assigned to the unique values in the domain
solve_singletons([]).
solve_singletons([dom(X,[X])|Doms]) :-
   solve_singletons(Doms).

% select(E,L,L1) selects the first element of
% L that matches E, with L1 being the remaining
% elements.
my_select(D,Doms,ODoms) :-
   select(D,Doms,ODoms), !.

% choose(E,L,L1) chooses an element of
% L that matches E, with L1 being the remaining
% elements.
choose(D,Doms,ODoms) :-
   select(D,Doms,ODoms).

% split(L,L1,L2) splits list L into two lists L1 and L2
% with the about same number of elements in each list.
split([],[],[]).
split([A],[A],[]).
split([A,B|R],[A|R1],[B|R2]) :-
   split(R,R1,R2).

/* -------------------------------------------------------------------*/


cs1(V, V). %A1 = A2
cs2(V1, V2) :- (V1 is V2 - 1; V2 is V1 - 1). %next
cs3(V1, V2) :- V1 is V2 + 1. %right


zebra(English,Spaniard,Ukrainian,Norwegian,Japanese,
       Red,Green,Ivory,Yellow,Blue,
       Dog,Snails,Fox,Horse,Zebra,
       Coffee,Tea,Milk,Orange_Juice,Water,
       Old_Gold,Kools,Chesterfields,Lucky_Strike,Parliaments) :-

 csp([dom(English, [1, 2, 3, 4, 5]),
     dom(Spaniard, [1, 2, 3, 4, 5]),
     dom(Ukrainian, [1, 2, 3, 4, 5]),
     dom(Norwegian, [1, 2, 3, 4, 5]),
     dom(Japanese, [1, 2, 3, 4, 5]),
     dom(Red, [1, 2, 3, 4, 5]),
     dom(Green, [1, 2, 3, 4, 5]),
     dom(Ivory, [1, 2, 3, 4, 5]),
     dom(Yellow, [1, 2, 3, 4, 5]),
     dom(Blue, [1, 2, 3, 4, 5]),
     dom(Dog, [1, 2, 3, 4, 5]),
     dom(Snails, [1, 2, 3, 4, 5]),
     dom(Fox, [1, 2, 3, 4, 5]),
     dom(Horse, [1, 2, 3, 4, 5]),
     dom(Zebra, [1, 2, 3, 4, 5]),
     dom(Coffee, [1, 2, 3, 4, 5]),
     dom(Tea, [1, 2, 3, 4, 5]),
     dom(Milk, [1, 2, 3, 4, 5]),
     dom(Orange_Juice, [1, 2, 3, 4, 5]),
     dom(Water, [1, 2, 3, 4, 5]),
     dom(Old_Gold, [1, 2, 3, 4, 5]),
     dom(Kools, [1, 2, 3, 4, 5]),
     dom(Chesterfields, [1, 2, 3, 4, 5]),
     dom(Lucky_Strike, [1, 2, 3, 4, 5]),
     dom(Parliaments, [1, 2, 3, 4, 5])],
     [rel([English, Red], cs1(English,Red)),
     rel([Spaniard, Dog], cs1(Spaniard,Dog)),
     rel([Coffee, Green], cs1(Coffee,Green)),
     rel([Ukrainian, Tea], cs1(Ukrainian,Tea)),
     rel([Green, Ivory], cs3(Green,Ivory)),
     rel([Old_Gold, Snails], cs1(Old_Gold,Snails)),
     rel([Kools, Yellow], cs1(Kools,Yellow)),
     rel([Milk, Milk], Milk = 3),
     rel([Norwegian, Norwegian], Norwegian = 1), %here is the problem
     rel([Chesterfields, Fox], cs2(Chesterfields,Fox)),
     rel([Kools, Horse], cs2(Kools,Horse)),
     rel([Lucky_Strike, Orange_juice], cs1(Lucky_Strike,Orange_juice)),
     rel([Japanese, Parliaments], cs1(Japanese,Parliaments)),
     rel([Norwegian, Blue], cs2(Norwegian,Blue))]).

1 个答案:

答案 0 :(得分:2)

我做了一些搜索,然后我建议一些reading a constraint satisfaction using arc consistencysample data

再次编辑到目前为止的努力。唉,添加最后一个约束会使结果无效。明天我会试着理解为什么

好消息!! 我在next / 2

中找到了愚蠢的错误
:- include(csp).

next(V1, V2) :-
    succ(V1, V2) ; succ(V2, V1).

dom(I, O, D) :-
    maplist(dom, I, O),
    alldiff(I, [], D).
dom(V, dom(V, [1,2,3,4,5])).

alldiff([], D, D).
alldiff([V|Vs], S, D) :-
    maplist(rdiff(V), Vs, Ds),
    append(S, Ds, As),
    alldiff(Vs, As, D).

rdiff(A, B, D) :- rel(A \= B, D).

rel(R, rel([A, B], R)) :- R =.. [_, A, B].

zebra :-
    People = [English, Spaniard, Ukrainian, Norwegian, Japanese],
    Color = [Red, Green, Ivory, Yellow, Blue],
    Pet = [Dog, Snails, Fox, Horse, Zebra],
    Drink = [Coffee, Tea, Milk, Orange_Juice, _Water],
    Smoke = [Old_Gold, Kools, Chesterfields, Lucky_Strike, Parliaments],
    maplist(dom, [People, Color, Pet, Drink, Smoke], DomT, DiffPair),
    flatten(DomT, Doms),
    maplist(rel,
        [English = Red % The Englishman lives in the red house
        ,Spaniard = Dog % The Spaniard owns the dog
        ,Ukrainian = Tea % The Ukrainian drinks tea
        ,Coffee = Green % Coffee is drunk in the green house
        ,succ(Ivory, Green) % The green house is immediately to the right of the ivory house
        ,Old_Gold = Snails % The Old Gold smoker owns snails
        ,Kools = Yellow % Kools are smoked in the yellow house
        ,Milk = H3 % Milk is drunk in the middle house
        ,Norwegian = H1 % The Norwegian lives in the first house
        ,next(Chesterfields, Fox) % The man who smokes Chesterfield lives in the house next to the man with the fox
        ,next(Kools, Horse) % Kools are smoked in the house next to the house where the horse is kept
        ,Lucky_Strike = Orange_Juice % The Lucky Strike smoker drinks orange juice
        ,Japanese = Parliaments % The Japanese smokes Parliament
        ,next(Norwegian, Blue) % The Norwegian lives next to the blue house
        ], ConstrS),
    flatten([DiffPair, ConstrS], Rels),
    csp([dom(H1, [1]), dom(H3, [3])|Doms], Rels),
    maplist(writeln,
        [people:[English, Spaniard, Ukrainian, Norwegian, Japanese],
         color:[Red, Green, Ivory, Yellow, Blue],
         pet:[Dog, Snails, Fox, Horse, Zebra],
         drink:[Coffee, Tea, Milk, Orange_Juice, _Water],
         smoke:[Old_Gold, Kools, Chesterfields, Lucky_Strike, Parliaments]
        ]).

我已将csp.pl分开,改编为SWI-Prolog。这是

% Computational Intelligence: a logical approach.
% Prolog Code.
% A CSP solver using arc consistency (Figure 4.8)
% Copyright (c) 1998, Poole, Mackworth, Goebel and Oxford University Press.

% csp(Domains, Relations) means that each variable has
% an instantiation to one of the values in its Domain
% such that all the Relations are satisfied.
% Domains represented as list of
% [dom(V,[c1,...,cn]),...]
% Relations represented as [rel([X,Y],r(X,Y)),...]
%  for some r
csp(Doms,Relns) :-
   ac(Doms,Relns).

% ac(Dom,Relns) is true if the domain constrants
% specified in Dom and the binary relations
% constraints specified in Relns are satisfied.
ac(Doms,Relns) :-
   make_arcs(Relns,A),
   consistent(Doms,[],A,A).

% make_arcs(Relns, Arcs) makes arcs Arcs corresponding to
% relations Relns. There are acrs for each ordering of
% variables in a relations.
make_arcs([],[]).
make_arcs([rel([X,Y],R)|O],
          [rel([X,Y],R),rel([Y,X],R)|OA]) :-
   make_arcs(O,OA).

% consistent(Doms,CA,TDA,A) is true if
% Doms is a set of domains
% CA is a set of consistent arcs,
% TDA is a list of arcs to do
% A is a list of all arcs
consistent(Doms,CA,TDA,A) :-
   consider(Doms,RedDoms,CA,TDA),
   solutions(RedDoms,A).

% consider(D0,D1,CA,TDA)
% D0 is the set of inital domains
% D1 is the set of reduced domains
% CA = consistent arcs,
% TDA = to do arcs
consider(D,D,_,[]).
consider(D0,D3,CA,[rel([X,Y],R)|TDA]) :-
   choose(dom(XV,DX),D0,D1),X==XV,
   choose(dom(YV,DY),D1,_),Y==YV, !,
   prune(X,DX,Y,DY,R,NDX),
   ( NDX = DX
   ->
     consider(D0,D3,[rel([X,Y],R)|CA],TDA)
   ; acc_todo(X,Y,CA,CA1,TDA,TDA1),
     consider([dom(X,NDX)|D1],D3,
              [rel([X,Y],R)|CA1],TDA1)).

% prune(X,DX,Y,DY,R,NDX)
% variable X had domain DX
% variable Y has domain DY
% R is a relation on X and Y
% NDX = {X in DX | exists Y such that R(X,Y) is true}
prune(_,[],_,_,_,[]).
prune(X,[V|XD],Y,YD,R,XD1):-
   \+ (X=V,member(Y,YD),R),!,
   prune(X,XD,Y,YD,R,XD1).
prune(X,[V|XD],Y,YD,R,[V|XD1]):-
   prune(X,XD,Y,YD,R,XD1).

% acc_todo(X,Y,CA,CA1,TDA,TDA1)
% given variables X and Y,
% updates consistent arcs from CA to CA1 and
% to do arcs from TDA to TDA1
acc_todo(_,_,[],[],TDA,TDA).
acc_todo(X,Y,[rel([U,V],R)|CA0],
         [rel([U,V],R)|CA1],TDA0,TDA1) :-
   ( X \== V
   ; X == V,
     Y == U),
   acc_todo(X,Y,CA0,CA1,TDA0,TDA1).
acc_todo(X,Y,[rel([U,V],R)|CA0],
         CA1,TDA0,[rel([U,V],R)|TDA1]) :-
   X == V,
   Y \== U,
   acc_todo(X,Y,CA0,CA1,TDA0,TDA1).

% solutions(Doms,Arcs) given a reduced set of
% domains, Dome, and arcs Arcs, solves the CSP.
solutions(Doms,_) :-
   solve_singletons(Doms).
solutions(Doms,A) :-
   select(dom(X,[XV1,XV2|XVs]),Doms,ODoms),
   split([XV1,XV2|XVs],DX1,DX2),
   acc_todo(X,_,A,CA,[],TDA),
   ( consistent([dom(X,DX1)|ODoms],CA,TDA,A)
   ; consistent([dom(X,DX2)|ODoms],CA,TDA,A)).

% solve_singletons(Doms) is true if Doms is a
% set of singletom domains, with the variables
% assigned to the unique values in the domain
solve_singletons([]).
solve_singletons([dom(X,[X])|Doms]) :-
   solve_singletons(Doms).

:- redefine_system_predicate(select(_,_,_)).

% select(E,L,L1) selects the first element of
% L that matches E, with L1 being the remaining
% elements.
select(D,Doms,ODoms) :-
%   remove(D,Doms,ODoms), !.
   system:select(D,Doms,ODoms), !.

% choose(E,L,L1) chooses an element of
% L that matches E, with L1 being the remaining
% elements.
choose(D,Doms,ODoms) :-
%   remove(D,Doms,ODoms).
    system:select(D,Doms,ODoms).

% split(L,L1,L2) splits list L into two lists L1 and L2
% with the about same number of elements in each list.
split([],[],[]).
split([A],[A],[]).
split([A,B|R],[A|R1],[B|R2]) :-
   split(R,R1,R2).
在对下一个/ 2进行最后修正后,测试似乎很好:

?- zebra.
people:[3,4,2,1,5]
color:[3,5,4,1,2]
pet:[4,3,1,2,5]
drink:[5,2,3,4,1]
smoke:[3,1,2,4,5]
true ;
false.