Prolog Logic / Einstein Puzzle

时间:2013-11-14 02:05:32

标签: prolog logic zebra-puzzle

问题是

布朗,克拉克,琼斯和史密斯是四个重要的公民,他们作为建筑师,银行家,医生和律师为社区服务,但不一定分别。 比琼斯更保守但比史密斯更自由的布朗,比年龄比他年长的男性更好的高尔夫球手,收入高于比克拉克年轻的男性

赚取超过建筑师的银行家既不是最年轻也不是最年长的。

医生是比律师更差的高尔夫球手,不如建筑师保守

正如所预料的那样,最年长的人是最保守的,收入最多的人,最年轻的男人是最好的高尔夫球手

每个男人的职业是什么?

我写过

jobs(L) :- L = [[brown,_,_,_,_,_],
           [clark,_,_,_,_,_],
           [jones,_,_,_,_,_],
           [smith,_,_,_,_,_]],
        % [name,job,conservative,golf,income,age]
        % conserative: 1 = least conservative, 4 = most conservative
        % golf: 1 = worst golfer, 4 = best golfer
        % income: 1 = least income, 4 = highest income
        % age: 1 = youngest, 4 = oldest

        % Brown is more conservative than Jones. Brown is less conservative than Smith.
        member([brown,_,C1,_,_,_],L),
        member([jones,_,C2,_,_,_],L),
        C1 > C2,
        member([smith,_,C3,_,_,_],L),
        C1 < C3,

        % Brown is a better golfer than those older than him.
        member([brown,_,_,G1,_,A1],L),
        member([_,_,_,G2,_,A2],L),
        G1 > G2, 
        A2 > A1,

        % Brown has a higher income than those younger than Clark.
        member([brown,_,_,_,I1,_],L),
        member([clark,_,_,_,_,A3],L),
        member([_,_,_,_,I2,A4],L),
        I1 > I2,
        A3 > A4,

        % Banker has a higher income than architect. Banker is neither youngest nor oldest.
        member([_,banker_,_,I3,A5],L),
        member([_,architect,_,_,I4,_],L),
        I3 > I4,
        (A5 = 2;A5 = 3),

        % Doctor is a worse golfer than lawyer. Doctor is less conservative than architect.
        member([_,doctor,C4,G3,_,_],L),
        member([_,lawyer,_,G4,_,_],L),
        member([_,architect,C5,_,_,_],L),
        G3 < G4,
        C4 < C5,

        % Oldest is most conservative and has highest income.
        member([_,_,4,_,4,4],L),

        % Youngest is the best golfer.
        member([_,_,_,4,_,1],L).

当我问它时

?- jobs(L).

我得到了

ERROR: >/2: Arguments are not sufficiently instantiated

我不确定这个错误意味着什么,我相信我已经翻译了所有的线索。

5 个答案:

答案 0 :(得分:2)

(继续由CapelliC开辟的道路......)从域中选择(更好的是,)应用规则通常是进入这些难题的方法。 尽快小心测试,尽快消除错误的选择 - 但不是更快。

我们无法对未知值进行算术比较,这就是错误的含义:>比较两个已知的算术值,其参数被实例化。但是如果Prolog逻辑变量尚未实例化,则意味着其值仍然未知。

约束逻辑编程(CLP)中,我们可以预先注册这些约束,但不能在vanilla Prolog中注册。虽然很多现代Prolog都有CLP包或谓词。 SWI Prolog也有它。但在香草Prolog代码中,我们必须小心。

mselect([A|As],S,Z):- select(A,S,S1), mselect(As,S1,Z).
mselect([],Z,Z).         %// instantiate a domain by selecting from it

puzzle(L):- %// [_,_,Conserv,Golf,Income,Age]
  L =      [ [brown,_,C1,G1,I1,A1],
             [clark,_,C2,_ ,I2,A2],
             [jones,_,C3,_ ,I3,A3],
             [smith,_,C4,_ ,I4,A4] ],

  L1 = [[_,_,4,_,4,4], [_,_,_,4,_,1]],           %// 6,7 - oldest, youngest
  mselect( L1, L, L2),                           %// L2: neither youngest nor oldest
  mselect( [A3,A4], [1,2,3,4], [A2,A1]), A2 > 1, %// 3b. 1 < A2 < A1 
  select( C2, [1,2,3,4], [C3,C1,C4]),            %// 1.  C3 < C1 < C4

  select(    [_, banker, _ ,GB,IB,_ ], L2, [P3] ),
  mselect( [ [_, archct, CA,GA,IA,_ ],           %// second view into the same matrix
             [_, doctor, CD,GD,ID,_ ] ], [P3|L1], 
           [ [_, lawyer, _ ,GL,IL,_ ] ]         ),
  CD < CA,                                       %// 5b.    
  mselect( [ID,IL], [1,2,3,4], [IA,IB]),         %// 4a.  IA < IB 
  mselect( [GA,GB], [1,2,3,4], [GD,GL]),         %// 5a.  GD < GL 

  %// 2. ( X in L : A1 < AX ) => G1 > GX
  %// 3. ( Y in L : AY < A2 ) => I1 > IY ... so, not(A1<A2)! i.e. % 3b. 1 < A2 < A1
  forall( (member(X,L), last(X,AX), AX>A1), (nth1(4,X,GX), G1>GX) ),
  forall( (member(Y,L), last(Y,AY), A2>AY), (nth1(5,Y,IY), I1>IY) ).

测试: ([_,_,Conserv,Golf,Income,Age])

7 ?- time(( puzzle(_X), maplist(writeln,_X),nl, false; true )).
[brown,banker,3,3,3,3]
[clark,doctor,1,1,1,2]
[jones,archct,2,4,2,1]
[smith,lawyer,4,2,4,4]

[brown,banker,3,3,3,3]
[clark,doctor,1,1,2,2]
[jones,archct,2,4,1,1]
[smith,lawyer,4,2,4,4]

[brown,banker,3,3,2,3]
[clark,doctor,1,1,3,2]
[jones,archct,2,4,1,1]
[smith,lawyer,4,2,4,4]

%// 2,299 inferences, 0.000 CPU in 0.120 seconds (0% CPU, Infinite Lips)
true.

根据问题的提问方式,这实际上是一个解决方案。

答案 1 :(得分:2)

如果您只使用有限域约束而不是低级算术,则代码的工作方式与预期完全相同。例如,使用(#>)/2代替(>)/2

通过使用约束使其超出此实例化错误之后,您会注意到除其他外,您的代码有一个拼写错误:banker_。此外,您没有正确制定暗示,因此您的谓词将产生false

以下是您的代码的略微修改版本,更改为使用有限域约束并纠正上述两个错误:

:- use_module(library(clpfd)).

older_worse_golfer([], _, _).
older_worse_golfer([[_,_,_,G,_,A]|Rest], G0, A0) :-
        A #> A0 #==> G #< G0,
        older_worse_golfer(Rest, G0, A0).

younger_higher_income([], _, _).
younger_higher_income([[_,_,_,_,I,A]|Rest], I0, A0) :-
        A #< A0 #==> I0 #> I,
        younger_higher_income(Rest, I0, A0).

man_profession_rest([M,P|Rest], M-P, Rest).

jobs(Ls, Vs) :-
        Ls = [[brown,_,_,_,_,_],
              [clark,_,_,_,_,_],
              [jones,_,_,_,_,_],
              [smith,_,_,_,_,_]],
        maplist(man_profession_rest, Ls, _, Rests),
        append(Rests, Vs),
        Vs ins 1..4,

        % [name,job,conservative,golf,income,age]
        % conserative: 1 = least conservative, 4 = most conservative
        % golf: 1 = worst golfer, 4 = best golfer
        % income: 1 = least income, 4 = highest income
        % age: 1 = youngest, 4 = oldest

        % Oldest is most conservative and has highest income.
        member([_,_,4,_,4,4], Ls),

        % Brown is more conservative than Jones. Brown is less
        % conservative than Smith.
        memberchk([brown,_,C1,_,_,_], Ls),
        memberchk([jones,_,C2,_,_,_], Ls),
        memberchk([smith,_,C3,_,_,_], Ls),
        C1 #> C2,
        C1 #< C3,

        % Brown is a better golfer than those older than him.
        memberchk([brown,_,_,G1,_,A1], Ls),
        older_worse_golfer(Ls, G1, A1),

        % IMPLIED: Brown is not the oldest
        A1 #< 4,

        % Brown has a higher income than those younger than Clark.
        memberchk([brown,_,_,_,I1,_], Ls),
        memberchk([clark,_,_,_,_,A3], Ls),
        younger_higher_income(Ls, I1, A3),

        % IMPLIED: Clark is not the youngest
        A3 #> 1,

        % Banker has a higher income than architect. Banker is neither
        % youngest nor oldest.
        I3 #> I4,
        A5 in 2..3,
        member([_,banker,_,_,I3,A5], Ls),
        member([_,architect,_,_,I4,_], Ls),

        % Doctor is a worse golfer than lawyer. Doctor is less
        % conservative than architect.
        member([_,doctor,C4,G3,_,_], Ls),
        member([_,lawyer,_,G4,_,_], Ls),
        member([_,architect,C5,_,_,_], Ls),
        G3 #< G4,
        C4 #< C5,

        % Youngest is the best golfer.
        member([_,_,_,4,_,1], Ls).

您可以使用label/1搜索具体的解决方案。正如您在以下查询中所看到的,有一个与职业相关的独特解决方案:

?- time(setof(MP, Ls^Vs^Rs^(jobs(Ls, Vs),
                            label(Vs),
                            maplist(man_profession_rest, Ls, MP, Rs)), MP)).

产生:

% 124,485 inferences, 0.041 CPU in 0.042 seconds (97% CPU, 3043643 Lips)
MP = [[brown-banker, clark-doctor, jones-architect, smith-lawyer]].

这甚至没有要求所有收入水平等都不同。如果需要,可以通过添加以下内容轻松表达此约束:

        transpose(Rests, RestsT), maplist(all_different, RestsT)

这个表述。

答案 2 :(得分:1)

您需要先将变量绑定到才能使用它们,最简单的方法是置换/ 2:

    L = [   [brown,J1,C1,G1,I1,A1],
            [clark,J2,C2,G2,I2,A2],
            [jones,J3,C3,G3,I3,A3],
            [smith,J4,C4,G4,I4,A4]],

permutation([1,2,3,4], [C1,C2,C3,C4]),
permutation([1,2,3,4], [I1,I2,I3,I4]),
permutation([1,2,3,4], [A1,A2,A3,A4]),
permutation([1,2,3,4], [G1,G2,G3,G4]),
permutation([banker,archit,doctor,lawyer], [J1,J2,J3,J4]),

现在可以使用规则

    % Brown is more conservative than Jones. Brown is less conservative than Smith.
    member([brown,_,CB,GB,IB,AB],L),
    member([jones,_,CJ,_,_,_],L),
    CB > CJ,
    member([smith,_,CS,_,_,_],L),
    CB < CS,

效率方面,当你选择(通过成员)一个命名成员时,一次“获取”所有相关变量(稍后使用棕色属性)。还要注意,在不同的选择变量J1,C1等中引用可能会导致不需要的绑定。

难以表达的规则是

    % Brown is a better golfer than those older than him.

    member([_,_,_,GO1,_,AO1],L),
    (AO1 > AB, GB > GO1 ; AO1 < AB),
    member([_,_,_,GO2,_,AO2],L),
    (AO2 > AB, GB > GO2 ; AO2 < AB),
    member([_,_,_,GO3,_,AO3],L),
    (AO3 > AB, GB > GO3 ; AO3 < AB),

    vardiff(GO1,GO2,GO3),
    vardiff(AO1,AO2,AO3),  % bug: AO1 was GO1

其中vardiff / 3是一个简单的方便:

vardiff(A,B,C) :- A\=B,A\=C,B\=C.

当然,如果您的Prolog可用,CLP(FD)是一个更好的选择。

答案 3 :(得分:1)

以下是我对问题的回答:

puzzle(Puzzle) :-
    Names = [brown,clark,jones,smith],

    permute(Names,Conservatives),

% Brown is more conservative than Jones.
    ismore(brown,jones,Conservatives),

% Brown is less conservative than Smith.
    isless(brown,smith,Conservatives),

    permute(Names,Golfs),
    permute(Names,Ages),

% Brown is a better golfer than those older than him.
    worsethans(brown,Golfs,WorseAtGolfThanBrown),
    betterthans(brown,Ages,OlderThanBrown),
    members(OlderThanBrown,WorseAtGolfThanBrown),

    permute(Names,Incomes),

% Brown has a higher income than those younger than Clark.
    worsethans(brown,Incomes,WorseIncomeThanBrown),
    worsethans(clark,Ages,YoungerThanClark),
    members(YoungerThanClark,WorseIncomeThanBrown),

    permute([banker,architect,lawyer,doctor],Jobs),

% Banker has a higher income than architect.
    lookup(banker,Jobs,Names,Banker),
    lookup(architect,Jobs,Names,Architect),
    ismore(Banker,Architect,Incomes),

% Banker is neither youngest nor oldest.
    ([_,Banker,_,_]=Ages;[_,_,Banker,_]=Ages),

% Doctor is a worse golfer than lawyer.
    lookup(doctor,Jobs,Names,Doctor),
    lookup(lawyer,Jobs,Names,Lawyer),
    ismore(Lawyer,Doctor,Golfs),

% Doctor is less conservative than architect.
    ismore(Architect,Doctor,Conservatives),

% Oldest is most conservative and has highest income.
    [Oldest,_,_,_]=Ages,
    [Oldest,_,_,_]=Conservatives,
    [Oldest,_,_,_]=Incomes,

% Youngest is the best golfer.
    [_,_,_,Youngest]=Ages,
    [Youngest,_,_,_]=Golfs,

    Puzzle = [Names,Jobs,c(Conservatives),g(Golfs),i(Incomes),a(Ages)].

它需要这些支持谓词:

ismore(X,Y,Zs) :-
    append(Xs,[Y|_],Zs),
    member(X,Xs).

isless(X,Y,Zs) :-
    append(_,[Y|Xs],Zs),
    member(X,Xs).

betterthans(X,Ys,Zs) :-
    append(Zs,[X|_],Ys).

worsethans(X,Ys,Zs) :-
    append(_,[X|Zs],Ys).

%lookup(X,Xs,Ys,Y)
lookup(X,[X|_],[Y|_],Y).
lookup(X,[_|Xs],[_|Ys],Y) :-
    lookup(X,Xs,Ys,Y).

members([], _).
members([X|Xs], Ys) :-
    member(X, Ys),
    members(Xs, Ys).

select([X|Xs], X, Xs).
select([X|Xs], Y, [X|Ys]) :- select(Xs, Y, Ys).

permute([], []).
permute(Xs, [X|Zs]) :-
    select(Xs, X, Ys),
    permute(Ys, Zs).

现在,我唯一的问题是这给了我不止一个答案。除非我的逻辑错误,否则这就是我所得到的:

[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,brown,clark,jones]),g([clark,brown,smith,jones]),i([smith,brown,clark,jones]),a([smith,brown,jones,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,brown,clark,jones]),g([clark,brown,smith,jones]),i([smith,brown,jones,clark]),a([smith,brown,jones,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,brown,clark,jones]),g([clark,brown,smith,jones]),i([smith,jones,brown,clark]),a([smith,brown,jones,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,brown,clark,jones]),g([clark,brown,smith,jones]),i([smith,brown,clark,jones]),a([smith,jones,brown,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,brown,clark,jones]),g([clark,brown,smith,jones]),i([smith,brown,jones,clark]),a([smith,jones,brown,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,brown,clark,jones]),g([clark,brown,smith,jones]),i([smith,jones,brown,clark]),a([smith,jones,brown,clark])]
[[brown,clark,jones,smith],[banker,doctor,architect,lawyer],c([smith,brown,jones,clark]),g([jones,brown,smith,clark]),i([smith,brown,clark,jones]),a([smith,brown,clark,jones])]
[[brown,clark,jones,smith],[banker,doctor,architect,lawyer],c([smith,brown,jones,clark]),g([jones,brown,smith,clark]),i([smith,brown,jones,clark]),a([smith,brown,clark,jones])]
[[brown,clark,jones,smith],[banker,doctor,architect,lawyer],c([smith,brown,jones,clark]),g([jones,brown,smith,clark]),i([smith,clark,brown,jones]),a([smith,brown,clark,jones])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,clark,brown,jones]),g([clark,brown,smith,jones]),i([smith,brown,clark,jones]),a([smith,brown,jones,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,clark,brown,jones]),g([clark,brown,smith,jones]),i([smith,brown,jones,clark]),a([smith,brown,jones,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,clark,brown,jones]),g([clark,brown,smith,jones]),i([smith,jones,brown,clark]),a([smith,brown,jones,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,clark,brown,jones]),g([clark,brown,smith,jones]),i([smith,brown,clark,jones]),a([smith,jones,brown,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,clark,brown,jones]),g([clark,brown,smith,jones]),i([smith,brown,jones,clark]),a([smith,jones,brown,clark])]
[[brown,clark,jones,smith],[banker,architect,doctor,lawyer],c([smith,clark,brown,jones]),g([clark,brown,smith,jones]),i([smith,jones,brown,clark]),a([smith,jones,brown,clark])]

如果我还说克拉克的收入大于布朗的收入,我可以将其限制在一个解决方案中。

任何人都可以确认我的答案是否正确以及是否应该有更多限制?

答案 4 :(得分:0)

我的解决方案基于CapelliC所说的

% [name,job,conservative,golf,income,age]
% conserative: 1 = least conservative, 4 = most conservative
% golf: 1 = worst golfer, 4 = best golfer
% income: 1 = lowest income, 4 = highest income
% age: 1 = youngest, 4 = oldest

jobs(L) :- L = 
       [[brown,J1,C1,G1,I1,A1],
        [clark,J2,C2,G2,I2,A2],
        [jones,J3,C3,G3,I3,A3],
        [smith,J4,C4,G4,I4,A4]],

        permutation([1,2,3,4], [C1,C2,C3,C4]),
        permutation([1,2,3,4], [I1,I2,I3,I4]),
        permutation([1,2,3,4], [A1,A2,A3,A4]),
        permutation([1,2,3,4], [G1,G2,G3,G4]),
        permutation([banker,architect,doctor,lawyer], [J1,J2,J3,J4]),

        % Brown is more conservative than Jones. Brown is less conservative than Smith.
        member([brown,_,CB,GB,IB,AB],L),
        member([jones,_,CJ,_,_,_],L),
        member([smith,_,CS,_,_,_],L),
        CB > CJ,
        CB < CS,

        % Brown is a better golfer than those older than him.
        member([_,_,_,G01,_,A01],L),
        (A01 > AB, GB > G01 ; A01 < AB),
        member([_,_,_,G02,_,A02],L),
        (A02 > AB, GB > G02 ; A02 < AB),
        member([_,_,_,G03,_,A03],L),
        (A03 > AB, GB > G03 ; A03 < AB),

        vardiff(G01,G02,G03),
        vardiff(G01,A02,A03),

        % Brown has a higher income than those younger than Clark.
        member([clark,_,_,_,_,AC],L),
        member([_,_,_,_,I01,A04],L),
        (A04 < AC, IB > I01; AC < A04),

        % Banker has a higher income than architect. Banker is neither youngest nor oldest.
        member([_,banker,_,_,IBa,ABa],L),
        member([_,architect,CAr,_,IAr,_],L),
        IBa > IAr,
        (ABa \= 1, ABa \= 4),

        % Doctor is a worse golfer than lawyer. Doctor is less conservative than architect.
        member([_,doctor,CDo,GDo,_,_],L),
        member([_,lawyer,_,GLa,_,_],L),
        GDo < GLa,
        CDo < CAr,

        % Oldest is most conservative and has highest income.
        member([_,_,4,_,4,4],L),

        % Youngest is the best golfer.
        member([_,_,_,4,_,1],L).

vardiff(A,B,C) :- A\=B, A\=C, B\=C.

我得到了

3 ?- jobs(L).
L = [[brown,architect,2,4,1,1],[clark,banker,3,1,2,2],[jones,doctor,1,2,3,3],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,2,2,2],[jones,doctor,1,1,3,3],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,3,2,2],[jones,doctor,1,1,3,3],[smith,lawyer,4,2,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,1,2,3],[jones,doctor,1,2,3,2],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,2,2,3],[jones,doctor,1,1,3,2],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,3,2,3],[jones,doctor,1,1,3,2],[smith,lawyer,4,2,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,1,3,2],[jones,doctor,1,2,2,3],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,2,3,2],[jones,doctor,1,1,2,3],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,3,3,2],[jones,doctor,1,1,2,3],[smith,lawyer,4,2,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,1,3,3],[jones,doctor,1,2,2,2],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,2,3,3],[jones,doctor,1,1,2,2],[smith,lawyer,4,3,4,4]] ;
L = [[brown,architect,2,4,1,1],[clark,banker,3,3,3,3],[jones,doctor,1,1,2,2],[smith,lawyer,4,2,4,4]] ;

多个答案重复,所以我删除了其中的一些。

所有线索都令人满意,除非Clark是第二老的例子

L = [[brown,architect,2,4,1,1],[clark,banker,3,1,2,3],[jones,doctor,1,2,3,2],[smith,lawyer,4,3,4,4]] ;

违反了这条线索

% Brown has a higher income than those younger than Clark.

对于我的所有答案,布朗是最年轻的,如

这样的线索
% Brown is a better golfer than those older than him.
% Brown has a higher income than those younger than Clark.
% Youngest is the best golfer.

似乎有点无意义......