我想解决冒险游戏中的一个谜题'不成文故事书2'在Prolog的帮助下,我可以进一步了解这种特定语言。
查找将添加到41的9个数字中的3个。
你必须选择一个数字三次。每次选择来自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
这是不正确的,因为在分组#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
任何帮助表示赞赏!
答案 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)
我会采取不同的方法。首先,我会选择更可操作的数据表示。数字与操作的每个配对:
[(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个列表作为单独的参数传递,但如果要更改列表数,则列表列表更具可伸缩性。我希望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