Prolog中的锁定挑战

时间:2017-02-21 16:59:14

标签: prolog

我刚开始学习prolog而且我一直试图解决这个难题:

Alt

我尝试添加一些规则,例如此示例http://swish.swi-prolog.org/example/houses_puzzle.pl,但我无法提出解决方案。

到目前为止我尝试了什么:

% Render the houses term as a nice table.
:- use_rendering(table,
         [header(h('N1', 'N2', 'N3'))]).
numbers(Hs) :-
    length(Hs, 1),
    member(h(6,8,2), Hs),
    member(h(6,1,4), Hs),
    member(h(2,0,6), Hs),
    member(h(7,3,8), Hs),
    member(h(7,8,0), Hs),
    correct_and_placed(6, 8, 2, Hs).

correct_and_place(A, B, C, R).

但我甚至不知道如何编写一条规则,可以检查一个数字是否正确并在正确的位置。

4 个答案:

答案 0 :(得分:3)

对于现有答案,我想使用 CLP(FD)约束添加一个版本。

我将使用的两个构建块是num_correct/3num_well_placed/3

首先,num_correct/3将两个整数列表与 common 元素的数量相关联:

num_correct(Vs, Ns, Num) :-
        foldl(num_correct_(Vs), Ns, 0, Num).

num_correct_(Vs, Num, N0, N) :-
        foldl(eq_disjunction(Num), Vs, 0, Disjunction),
        Disjunction #<==> T,
        N #= N0 + T.

eq_disjunction(N, V, D0, D0 #\/ (N #= V)).

示例查询:

?- num_correct([1,2,3], [3,5], Num).
Num = 1.

作为纯关系的特征,这也适用于更常见的查询,例如:

?- num_correct([A], [B], Num).
B#=A#<==>Num,
Num in 0..1.

其次,我使用num_well_placed/3,它将两个整数列表与相应元素相等的索引数相关联:

num_well_placed(Vs, Ns, Num) :-
        maplist(num_well_placed_, Vs, Ns, Bs),
        sum(Bs, #=, Num).

num_well_placed_(V, N, B) :- (V #= N) #<==> B.

再次,一个示例查询和答案:

?- num_well_placed([8,3,4], [0,3,4], Num).
Num = 2.

以下谓词只是将这两个结合起来:

num_correct_placed(Vs, Hs, C, P) :-
        num_correct(Vs, Hs, C),
        num_well_placed(Vs, Hs, P).

因此,整个谜题可以表述如下:

lock(Vs) :-
        Vs = [_,_,_],
        Vs ins 0..9,
        num_correct_placed(Vs, [6,8,2], 1, 1),
        num_correct_placed(Vs, [6,1,4], 1, 0),
        num_correct_placed(Vs, [2,0,6], 2, 0),
        num_correct_placed(Vs, [7,3,8], 0, 0),
        num_correct_placed(Vs, [7,8,0], 1, 0).

在这种情况下,根本不需要搜索

?- lock(Vs).
Vs = [0, 4, 2].

此外,如果我概括最后一个提示,即如果我写:

lock(Vs) :-
        Vs = [_,_,_],
        Vs ins 0..9,
        num_correct_placed(Vs, [6,8,2], 1, 1),
        num_correct_placed(Vs, [6,1,4], 1, 0),
        num_correct_placed(Vs, [2,0,6], 2, 0),
        num_correct_placed(Vs, [7,3,8], 0, 0),
        * num_correct_placed(Vs, [7,8,0], 1, 0).

然后,无需搜索即可确定的唯一解决方案:

?- lock(Vs).
Vs = [0, 4, 2].

事实上,我甚至可以拿走倒数第二个提示

lock(Vs) :-
        Vs = [_,_,_],
        Vs ins 0..9,
        num_correct_placed(Vs, [6,8,2], 1, 1),
        num_correct_placed(Vs, [6,1,4], 1, 0),
        num_correct_placed(Vs, [2,0,6], 2, 0),
        * num_correct_placed(Vs, [7,3,8], 0, 0),
        * num_correct_placed(Vs, [7,8,0], 1, 0).

仍然解决方案是唯一的,但我现在必须使用label/1来找到它:

?- lock(Vs), label(Vs).
Vs = [0, 4, 2] ;
false.

答案 1 :(得分:2)

我希望有更好的方法,但......

你可以实施&#34;一个号码是正确的,位置很好&#34;如下

oneRightPlace(X, Y, Z, X, S2, S3) :-
  \+ member(Y, [S2, S3]),
  \+ member(Z, [S2, S3]).

oneRightPlace(X, Y, Z, S1, Y, S3) :-
  \+ member(X, [S1, S3]),
  \+ member(Z, [S1, S3]).

oneRightPlace(X, Y, Z, S1, S2, Z) :-
  \+ member(X, [S1, S2]),
  \+ member(Y, [S1, S2]).

对于&#34;一个数字正确但放错了,您可以使用

oneWrongPlace(X, Y, Z, S1, S2, S3) :-
  member(X, [S2, S3]),
  \+ member(Y, [S1, S2, S3]),
  \+ member(Z, [S1, S2, S3]).

oneWrongPlace(X, Y, Z, S1, S2, S3) :-
  member(Y, [S1, S3]),
  \+ member(X, [S1, S2, S3]),
  \+ member(Z, [S1, S2, S3]).

oneWrongPlace(X, Y, Z, S1, S2, S3) :-
  member(Z, [S1, S2]),
  \+ member(X, [S1, S2, S3]),
  \+ member(Y, [S1, S2, S3]).

对于&#34;两个号码正确但放错了#34;您可以写

twoWrongPlace(X, Y, Z, S1, S2, S3) :-
  member(X, [S2, S3]),
  member(Y, [S1, S3]),
  \+ member(Z, [S1, S2, S3]).

twoWrongPlace(X, Y, Z, S1, S2, S3) :-
  member(X, [S2, S3]),
  member(Z, [S1, S2]),
  \+ member(Y, [S1, S2, S3]).

twoWrongPlace(X, Y, Z, S1, S2, S3) :-
  member(Y, [S1, S3]),
  member(Z, [S1, S2]),
  \+ member(X, [S1, S2, S3]).

并且,对于&#34;没有什么是正确的&#34;,变得简单

zeroPlace(X, Y, Z, S1, S2, S3) :-
  \+ member(X, [S1, S2, S3]),
  \+ member(Y, [S1, S2, S3]),
  \+ member(Z, [S1, S2, S3]).

现在你可以把所有的东西放到一边写

  member(S1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
  member(S2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
  member(S3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
  oneRightPlace(6, 8, 2, S1, S2, S3),
  oneWrongPlace(6, 1, 4, S1, S2, S3),
  twoWrongPlace(2, 0, 6, S1, S2, S3),
  zeroPlace(7, 3, 8, S1, S2, S3),
  oneWrongPlace(7, 8, 0, S1, S2, S3).

获取(S1S2S3)正确的解决方案。

前面的例子是在没有使用clp(fd)的情况下编写的,我不太了解但是(我想)可以很好地简化。

答案 2 :(得分:2)

因此,正如所有这些问题一样,我倾向于写一个通用的求解器而不是特定的求解器。借用我前一段时间写过的mastermind implementation(由这里的问题产生)我提出以下内容:

compare(List,Reference,RightPlace,WrongPlace)采用两个列表,并将RightPlace与出现在第二个列表中同一点的第一个列表的元素数量统一,并将WrongPlace与出现在第二个列表中不同点的元素数量进行统一。第二个列表(如果重复元素在两个列表中都重复,则仅计算重复元素)。它使用...

right_place(List,Reference,RightPlace)包装累加器并从每个列表的头部消耗元素,递增它们匹配的位置,并且......

any_match(List,Reference,Matches)包装一个消耗List列表头部的累加器,并尽可能从Reference列表中选择它,并在发生这种情况时递增。

WrongPlace然后从RightPlace的数量中减去Matches个元素的数量。

最后,find_solutions(Soln)使用clpfd在域(0-9)中创建元素列表,然后映射indomain以创建组合。然后使用forall将每个组合与每个提示进行比较,以确保满足所有提示约束。将所有内容与提示结合起来,即可得到:

:- use_module(library(clpfd)).

compare(List,Reference,RightPlace,WrongPlace) :-
    right_place(List,Reference,RightPlace),
    any_match(List,Reference,Matches),
    WrongPlace #= Matches - RightPlace.

right_place(List,Reference,RightPlace) :-
    right_place(List,Reference,0,RightPlace).

right_place([],[],RightPlace,RightPlace).
right_place([Match|List],[Match|Reference],Accumulator,RightPlace) :-
    NewAccumulator is Accumulator + 1,
    right_place(List,Reference,NewAccumulator,RightPlace).
right_place([A|List],[B|Reference],Accumulator,RightPlace) :-
    A \= B,
    right_place(List,Reference,Accumulator,RightPlace).

any_match(List,Reference,Matches) :-
    any_match(List,Reference,0,Matches).

any_match([],_,Matches,Matches).
any_match([Match|List],Reference,Accumulator,Matches) :-
    select(Match,Reference,NewReference),
    NewAccumulator is Accumulator + 1,
    any_match(List,NewReference,NewAccumulator,Matches).
any_match([Match|List],Reference,Accumulator,Matches) :-
    \+member(Match,Reference),
    any_match(List,Reference,Accumulator,Matches).

find_solutions(Soln) :-
    length(Soln,3),
    Soln ins 0..9,
    maplist(indomain,Soln),
    forall(hint(X,Y,Z),compare(Soln,X,Y,Z)).

hint([6,8,2],1,0).
hint([6,1,4],0,1).
hint([2,0,6],0,2).
hint([7,3,8],0,0).
hint([7,8,0],0,1).

答案 3 :(得分:1)

不确定我需要解释这么多。您生成所有可能性,然后编码约束。

code(A,B,C) :-
  member(A,[0,1,2,3,4,5,6,7,8,9]),
  member(B,[0,1,2,3,4,5,6,7,8,9]),
  member(C,[0,1,2,3,4,5,6,7,8,9]),
  ( A = 6 ; B = 8 ; C = 2 ),
  ( A = 1, \+ member(B,[6,4]), \+ member(C,[6,4])
  ; A = 4, \+ member(B,[6,1]), \+ member(C,[6,1])
  ; B = 6, \+ member(A,[1,4]), \+ member(C,[1,4])
  ; B = 4, \+ member(A,[6,1]), \+ member(C,[6,1])
  ; C = 6, \+ member(B,[1,4]), \+ member(A,[1,4])
  ; C = 1, \+ member(B,[6,4]), \+ member(A,[6,4]) ),
  ( A = 0, B = 2, C \= 6
  ; A = 0, B = 6, C \= 2
  ; A = 6, B = 2, C \= 0
  ; B = 2, C = 0, A \= 6
  ; B = 6, C = 2, A \= 0
  ; B = 6, C = 0, A \= 2
  ; C = 2, A = 0, B \= 6
  ; C = 2, A = 6, B \= 0
  ; C = 0, A = 6, B \= 2 ),
  \+ member(A,[7,3,8]), \+ member(B,[7,3,8]), \+ member(C,[7,3,8]),
  ( A = 8, \+ member(B,[7,0]), \+ member(C,[7,0])
  ; A = 0, \+ member(B,[7,8]), \+ member(C,[7,8])
  ; B = 7, \+ member(A,[8,0]), \+ member(C,[8,0])
  ; B = 0, \+ member(A,[7,8]), \+ member(C,[7,8])
  ; C = 7, \+ member(B,[8,0]), \+ member(A,[8,0])
  ; C = 8, \+ member(B,[7,0]), \+ member(A,[7,0]) ).

结果如下:

| ?- code(A,B,C).
A = 0,
B = 4,
C = 2 ? ;
no