选择加起来特定数字的数字(扭曲)

时间:2015-03-13 18:04:30

标签: prolog

我想解决冒险游戏中的一个谜题'不成文故事书2'在Prolog的帮助下,我可以进一步了解这种特定语言。

查找将添加到41的9个数字中的3个。

你必须选择一个数字三次。每次选择来自9个数字中的3个固定的不同分组。组的顺序是固定的。这些选项与减号或加号操作数组合,后者不是数字本身的符号,但会告诉它如何与下一个数字(扭曲)进行代数关联。

选择的示例组:

  1. (65 +) (17 - ) (37 +)

  2. (50 - ) (23 - ) (27 +)

  3. (33) (47) (45)

  4. 正确的解决方案是

    (65+) (23-) (47) = 41
    i.e. 65 + 23 - 47 = 41
    

    以下我的程序只能找到以下不正确的解决方案

    (17 - )(23 +)(47)= 41

    (17 - )(23 - )(47)= -53

    这是不正确的,因为在分组#2中只有(23 - )可用,而不是(23 +)。

    addX(X,Y,S) :- X is S - Y.
    
    e(X,S,R) :- ( (addX( X,65,      S),R='65 +') ; (addX( X,17, -1 * S),R = '17 -') ; (addX( X,37,S),R = '37 +') ),X=0.
    d(X,S,R) :-   (addX( X,50, -1 * S),R='50 -') ; (addX( X,23, -1 * S),R = '23 -') ; (addX( X,27,S),R = '27 +').
    c(X,S,R) :-   (addX( X,33,      S),R='33  ') ; (addX( X,47,      S),R = '47  ') ; (addX( X,45,S),R = '45  ').
    
    solve(Sum,Res5,Res4,Res3) :- c(Xc,Sum,Res3),d(Xd,Xc,Res4),e(Xe,Xd,Res5).
    
    ?- solve(41,X1,X2,X3).
    X1 = '17 -',
    X2 = '23 -',
    X3 = 47 ;
    false.
    

    问题是运算符未正确应用。 所以我尝试通过嵌套调用返回的符号来修复它。

    addX(X,Y,S) :- X is S - Y.
    
    e(X,S,R,SI,SO) :- ( (addX( X,65,S),R=65,SO= 1) ; (addX( X,17,S),R = 17,SO= -1) ; (addX( X,37,S),R = 37,SO= 1) ),X=0.
    d(X,S,R,SI,SO) :-   (addX( X,SI*50,S),R=50,SO= -1) ; (addX( X,SI*23,S),R = 23,SO= -1) ; (addX( X,SI*27,S),R = 27,SO= 1).
    c(X,S,R,SI,SO) :-   (addX( X,SI*33,S),R=33,SO= 1) ; (addX( X,SI*47,S),R = 47,SO= 1) ; (addX( X,SI*45,S),R = 45,SO= 1).
    
    solve(Sum,Res3,Res4,Res5) :- e(Xe,Xd,Res5,1,SO5),d(Xd,Xc,Res4,SO5,SO4),c(Xc,Sum,Res3,SO4,SO3).
    

    不幸的是,这导致以下运行时错误:

    ?- solve(41,X1,X2,X3).
    ERROR: is/2: Arguments are not sufficiently instantiated
    

    任何帮助表示赞赏!

3 个答案:

答案 0 :(得分:3)

一个'声明'的解决方案(或者是一个丑陋的黑客?)这个很好的谜题

:- op(100, xf, +).
:- op(100, xf, -).

test(X,Y,Z) :-
    member(X, [65 +, 17 -, 37 +]),
    member(Y, [50 -, 23 -, 27 +]),
    member(Z, [33  , 47  , 45  ]),
    combine(X, Y, Z, E), 41 is E.

combine(X -, Y -, Z, X - Y - Z).
combine(X -, Y +, Z, X - Y + Z).
combine(X +, Y -, Z, X + Y - Z).
combine(X +, Y +, Z, X + Y + Z).

更通用的解决方案令人惊讶地困难,尽管更短......

test(X,Y,Z) :-
    member(X, [65 +, 17 -, 37 +]),
    member(Y, [50 -, 23 -, 27 +]),
    member(Z, [33  , 47  , 45  ]),
    eval([X, Y, Z], E), 41 is E.
    %combine(X, Y, Z, E), 41 is E.

eval([H|T], E) :- H =.. [Op,N], eval(Op,N,T,E).
eval(Op,X,[H|T], E) :- 
   H =.. [Op1,M], Q =.. [Op,X,M], eval(Op1,Q,T,E)
 ; E =.. [Op,X,H].

答案 1 :(得分:3)

由于@ CapelliC的解决方案并不是一个丑陋的黑客,而且他和@ lurker都非常优雅和有用,我以为我取得了自由回答一个真正丑陋的黑客。

这肯定不是解决问题的好方法。但它确实有效!

puzzle(P) :-
    P = [
            ['65 +', '17 -', '37 +'],
            ['50 -', '23 -', '27 +'],
            ['33', '47', '45']
        ].

puzzle_solution(Puzzle, Sum, Expr) :-
    maplist(member, Ps, Puzzle),
    atomic_list_concat(Ps, ' ', ExprAtom),
    term_to_atom(Expr, ExprAtom),
    Sum is Expr.

答案 2 :(得分:2)

我会采取不同的方法。首先,我会选择更可操作的数据表示。数字与操作的每个配对:

  1. [(65,(+)), (17,(-)), (37, (+))]

  2. [(50, (-)), (23, (-)), (27, (+))]

  3. [33, 47, 45]

  4. 这里,数字/操作对是(N, (op))。这个术语将让我们很容易地选出数字和操作员。由于Prolog中的语法原因,运算符周围需要额外的括号。您也可以选择[[65,+], [17,-], [37,+]][t(65,+), t(17,-), t(37,+)]等表单。

    然后,为了查询问题,我选择将这组信息作为上述列表的列表传递。查询看起来像:

    solve(41, [[(65,(+)),(17,(-)),(37,(+))],[(50,(-)),(23,(-)),(27,(+))],[(33),(47),(45)]], Result).
    

    我可以选择将上述3个列表作为单独的参数传递,但如果要更改列表数,则列表列表更具可伸缩性。我希望Result看到的是按顺序列出从上述3个列表中选择的一个项目的列表,以便对该列表进行评估。

    然后解决方案将遍历列表列表的每个元素,从每个元素中选择一个成员,评估该选择的结果,并将该结果与sum参数进行比较(在本例中为41

    %  Solve the summation problem
    
    solve(Sum, [C|Choices], [A|Results]) :-
        member(A, C),
        solve(Sum, A, Choices, Results).
    solve(Sum, (N1,Op1), [C|Choices], [(N2,Op2)|Results]) :-
        member((N2,Op2), C),
        Term =.. [Op1, N1, N2],
        S is Term,
        solve(Sum, (S,Op2), Choices, Results).
    solve(Sum, (N1,Op1), [C], [N2]) :-
        member(N2, C),
        Term =.. [Op1, N1, N2],
        Sum is Term.
    

    运行此查询:

    | ?-  solve(41, [[(65,(+)),(17,(-)),(37,(+))],[(50,(-)),(23,(-)),(27,(+))],[(33),(47),(45)]], R).
    
    R = [(65,(+)),(23,(-)),47] ? ;
    
    no
    

    通过对第一个参数使用变量,您可以看到结果的可能组合:

    | ?-  solve(S, [[(65,(+)),(17,(-)),(37,(+))],[(50,(-)),(23,(-)),(27,(+))],[(33),(47),(45)]], R).
    
    R = [(65,(+)),(50,(-)),33]
    S = 82 ? ;
    
    R = [(65,(+)),(50,(-)),47]
    S = 68 ? ;
    
    R = [(65,(+)),(50,(-)),45]
    S = 70 ? ;
    ...
    

    只需更改上面的1,2和3中的列表,即可轻松扩展此解决方案。您可以有两个或更多列表,也可以有不同的长度列表。

    <小时/> 如果我可以窃取病理学家maplist的想法,这个解决方案的变体将是:

    solve(Sum, Choices, Results) :-
        maplist(member, Results, Choices),
        evaluate(Results, Sum).
    
    evaluate([(N1,Op1), (N2,Op2)|Ops], Sum) :-
        Term =.. [Op1, N1, N2],              % form a term with first two
        S is Term,                           % Evaluate the Term
        evaluate([(S,Op2)|Ops], Sum).        % Evaluate the remaining terms
    evaluate([(N1,Op),N2], Sum) :-           % Evaluate last term
        Term =.. [Op, N1, N2],               % Form a term
        Sum is Term.                         % Check final sum