Prolog 选择与特定数字相加的数字(带扭曲)

Prolog 选择与特定数字相加的数字(带扭曲),prolog,Prolog,我想在Prolog的帮助下,解开冒险游戏《不成文故事集2》中的一个谜语,进一步加深我对这门语言的了解 从9个数字中找出3个加起来就是41 你必须选择一个数字三次。每次选择都来自9个数字中的3个固定的不同分组。组的顺序是固定的。这些选项与一个减号或加号操作数组合,该操作数不是数字本身的符号,而是告诉它将如何与下一个数字(扭曲)进行代数链接 要从中选择的示例组: (65+) (17 -) (37+) (50-) (23 -) (27+) (33) (47) (45) 正确的解决办法是 (65+)

我想在Prolog的帮助下,解开冒险游戏《不成文故事集2》中的一个谜语,进一步加深我对这门语言的了解

从9个数字中找出3个加起来就是41

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

要从中选择的示例组:

  • (65+) (17 -) (37+)

  • (50-) (23 -) (27+)

  • (33) (47) (45)

  • 正确的解决办法是

    (65+) (23-) (47) = 41
    i.e. 65 + 23 - 47 = 41
    
    我下面的程序只找到以下不正确的解决方案

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

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

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

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

    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
    

    感谢您的帮助

    我会采取不同的方法。首先,我会选择一种更易于操作的数据表示形式。编号与操作的每个配对:

  • [(65,(+),(17,(-),(37,(+)])]

  • [(50,(-),(23,(-),(27,(+)])]

  • [33,47,45]

  • 这里,数字/操作对是,
    (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个列表作为单独的参数传递,但是如果您想更改列表的数量,列表的列表更具可伸缩性。对于
    结果
    ,我希望看到的是从上述三个列表中依次选择一个项目的列表,因此该列表的评估值为41

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

    运行此查询:

    | ?-  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中的列表即可。您可以有两个或多个列表,也可以有不同长度的列表。
    如果我可以窃取aBathologists
    maplist
    idea,这个解决方案的一个变体是:

    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
    

    我会采取不同的方法。首先,我会选择一种更易于操作的数据表示形式。编号与操作的每个配对:

  • [(65,(+),(17,(-),(37,(+)])]

  • [(50,(-),(23,(-),(27,(+)])]

  • [33,47,45]

  • 这里,数字/操作对是,
    (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个列表作为单独的参数传递,但是如果您想更改列表的数量,列表的列表更具可伸缩性。对于
    结果
    ,我希望看到的是从上述三个列表中依次选择一个项目的列表,因此该列表的评估值为41

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

    运行此查询:

    | ?-  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中的列表即可。您可以有两个或多个列表,也可以有不同长度的列表。
    如果我可以窃取aBathologists
    maplist
    idea,这个解决方案的一个变体是:

    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
    
    一个“声明性”的解决方案(或者是一个丑陋的黑客?)

    :- 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].
    
    一个“声明性”的解决方案(或者是一个丑陋的黑客?)

    :- 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].
    

    既然@capelical的解决方案并没有被证明是一个丑陋的黑客,而他的和@潜伏者的都足够优雅和有用,我想我可以冒昧地用一个真正丑陋的黑客来回答

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

    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.
    

    既然@capelical的解决方案并没有被证明是一个丑陋的黑客,而他的和@潜伏者的都足够优雅和有用,我想我可以冒昧地用一个真正丑陋的黑客来回答

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

    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.
    

    如果从第三组中选择一个数字,该数字不能为负数(第三组中没有符号)?第三组中没有附加运算符,因为运算符不是该数字的符号,但说明以下数字将如何以代数方式与其关联,并且示例中没有第四组。不幸的是,我的代码中的组数是硬编码的,我希望我能够