Prolog 中的“建筑”之谜

Posted

技术标签:

【中文标题】Prolog 中的“建筑”之谜【英文标题】:A "Building" Riddle in Prolog 【发布时间】:2015-10-17 14:27:46 【问题描述】:

我正在尝试解决 Prolog 中的一个谜题。谜语是: 有两栋楼,每栋都有树屋(每层公寓):一套3房,一套4房,一套5房。

Dana、Joni 和 Noah 住​​在 1 号楼。 Ron、Gale 和 Aron 住在 2 号楼。

Joni 公寓高于 Dana 和 Noah。这意味着他住在1号楼的三楼。 诺亚和盖尔住在同一楼层(在不同的建筑物中)。 Ron 比 Aron 多一个房间。 罗恩住在盖尔楼上。 2号楼最高的公寓是5房公寓。

我需要找出每个人住在哪一层。

我写了这段代码:

solve([dana,building1,F1,R1],[noah,building1,F2,R2],[joni,building1,F3,R3],[gale,building2,F4,R4],[ron,building2,F5,R5],[aron,building2,F6,R6]
      ) :-
   L =[[dana,building1,1,3],[dana,building1,1,4],[dana,building1,1,5],[dana,building1,2,3],[dana,building1,2,4],[dana,building1,2,5],[dana,building1,3,3],[dana,building1,3,4],[dana,building1,3,5]
,[noah,building1,1,3],[noah,building1,1,4],[noah,building1,1,5],[noah,building1,2,3],[noah,building1,2,4],[noah,building1,2,5],[noah,building1,3,3],[noah,building1,3,4],[noah,building1,3,5]
,[joni,building1,1,3],[joni,building1,1,4],[joni,building1,1,5],[joni,building1,2,3],[joni,building1,2,4],[joni,building1,2,5],[joni,building1,3,3],[joni,building1,3,4],[joni,building1,3,5]
,[gale,building1,1,3],[gale,building1,1,4],[gale,building1,1,5],[gale,building1,2,3],[gale,building1,2,4],[gale,building1,2,5],[gale,building1,3,3],[gale,building1,3,4],[gale,building1,3,5]
,[ron,building1,1,3],[ron,building1,1,4],[ron,building1,1,5],[ron,building1,2,3],[ron,building1,2,4],[ron,building1,2,5],[ron,building1,3,3],[ron,building1,3,4],[ron,building1,3,5]
,[aron,building1,1,3],[aron,building1,1,4],[aron,building1,1,5],[aron,building1,2,3],[aron,building1,2,4],[aron,building1,2,5],[aron,building1,3,3],[aron,building1,3,4],[aron,building1,3,5]],
   F3 > F2,
   F3>F1,
   F2 == F4,
   R5 == R6-1,
   F5 == F4+1,
   (F4 == 3, R4 == 5;F5 == 3, R5 == 5; F6 == 3, R6 == 5),
   del([dana,building1,F1,R1],L,List1),
   del([noah,building1,F2,R2],List1,List2),
   del([joni,building1,F3,R3],List2,List3),
   del([gale,building2,F4,R4],List3,List4),
   del([ron,building2,F5,R5],List4,List5),
   del([aron,building2,F6,R6],List5,_).

del(X,L,L1) :-
   remove(X,L,L1).

但是当我执行查询时:

solve([dana,building1,F1,R1],[noah,building1,F2,R2],[joni,building1,F3,R3],[gale,building2,F4,R4],[ron,building2,F5,R5],[aron,building2,F6,R6]).

我明白了:

"Error 22 : Instantiation Error"

有人吗? 我不明白我做错了什么。

【问题讨论】:

您要执行的查询是什么?这些(您发布的)是您拥有的所有事实和规则吗?如果你试图从你的目标中画出证明树,结果就是tono @DebasishJana 我现在在问题末尾添加了我执行的查询,我发布的都是我拥有的所有事实和规则。我会试着画一棵证明树.. (这与zebra-puzzle 有一些相似之处,尽管它不完全是一个) @false 我觉得很有帮助 :) 谢谢。 【参考方案1】:

我终于做到了。 这就是答案:

solve([dana,building1,F1,R1],
      [noah,building1,F2,R2],
      [joni,building1,F3,R3],
      [gale,building2,F4,R4],
      [ron,building2,F5,R5],
      [aron,building2,F6,R6]) :-
   assign([1,2,3],[F1,F2,F3]),
   assign([1,2,3],[F4,F5,F6]),
   assign([3,4,5],[R1,R2,R3]),
   assign([3,4,5],[R4,R5,R6]),
   F3 > F2,
   F3>F1,
   F2 =:= F4,
   R5 =:= R6-1,
   F5 =:= F4+1,
   (  F4 =:= 3, R4 =:= 5
   ;  F5 =:= 3, R5 =:= 5
   ;  F6 =:= 3, R6 =:= 5
   ).

assign(_,[]).
assign(Digs,[D|Vars]):-
   del(D,Digs,Digs1),
   assign(Digs1,Vars).

del(X,L,L1):-
   remove(X,L,L1).

【讨论】:

重写您的解决方案以使用library(clpfd) 怎么样? (适用于 SICStus、SWI、YAP 等) @false 好主意。谢谢。 然后您可以声明F3 > F2 之前 assign/3!从而避免了很多回溯。 您还可以使用 CLP(FD) 约束来表达赋值:assignment(Ns, Vs) :- pairs_keys_values(Pairs, Ns, Os), maplist(=(1), Os), global_cardinality(Vs, Pairs). 这进一步有助于避免回溯。【参考方案2】:
% all zebra-like problems solved in CLP or ASP very easy.
% ECLiPSe CLP
:-lib(ic).
ap(Apart1,Rooms1,Apart2,Rooms2) :-
        Apart1 = [Dana,Joni,Noah], Rooms1 = [DanaRm,JoniRm,NoahRm],
        Apart2 = [Ron,Gale,Aron], Rooms2 = [RonRm,GaleRm,AronRm],
        Apart1 :: [1..3], Rooms1 :: [3..5],
        Apart2 :: [1..3], Rooms2 :: [3..5],
        alldifferent(Apart1), alldifferent(Rooms1),
        alldifferent(Apart2), alldifferent(Rooms2),
        Joni #= 3, % so Dana Noah = 1..2
        Noah #= Gale,
        RonRm #= AronRm + 1, % so RonRm = 4..5 and AronRm=3..4
        Ron #= Gale + 1, % so Ron = 2..3 and Gale=1..2
    % building2 3floor is 5room. Gale cant be 3. so Ron=3 or Aron=3.
    % but AronRm cant be 5 so Aron cant be 3. So Ron is 3 and RonRm is 5.
    % ...but in search of holy declarativity lets Prolog to figure that :-)
        ( Ron #= 3, RonRm #= 5
        ; Gale #= 3, GaleRm #=5
        ; Aron #= 3, AronRm #=5
        ),
        labeling(Apart1),labeling(Rooms1),
        labeling(Apart2),labeling(Rooms2).
solve:-
        ap(A1,R1,A2,R2),
        write(A1),write(R1),write(A2),write(R2),nl.
run :-
        statistics(hr_time, Start),
        (\+ (solve, false)),
        statistics(hr_time, End), Time is End - Start,
        write('All solutions found in '), write(Time), write(' seconds'), nl.
/*  hardware: pentium4-1mb-3.0ggz at ddr1-333mhz
    software: eclipse-6.2.24 on slackware-14.0
    [eclipse 1]: [apart].
    apart.pl   compiled 4160 bytes in 0.18 seconds
    [eclipse 2]: run.
    [1, 3, 2][3, 4, 5][3, 2, 1][5, 3, 4]
    [1, 3, 2][3, 5, 4][3, 2, 1][5, 3, 4]
    [1, 3, 2][4, 3, 5][3, 2, 1][5, 3, 4]
    [1, 3, 2][4, 5, 3][3, 2, 1][5, 3, 4]
    [1, 3, 2][5, 3, 4][3, 2, 1][5, 3, 4]
    [1, 3, 2][5, 4, 3][3, 2, 1][5, 3, 4]
    All solutions found in 0.00655293464660645 seconds
more or less, we have only 1 solution. but we not have any info about
size of rooms in first building. and because of that we just get all 6
possibly combinations for it.
*/

【讨论】:

纯代码的答案很少是好的。您可能希望移动 cmets 并将它们作为解释代码的段落。

以上是关于Prolog 中的“建筑”之谜的主要内容,如果未能解决你的问题,请参考以下文章

使用 Prolog 优化约束逻辑编程中的寻路

Prolog 中的爱因斯坦谜题

序言:谋杀之谜解决方案

Prolog递归方法

爱因斯坦谜语使用 Prolog

列表替换中的 Prolog 元素