选择加起来等于特定数字的数字(扭曲)
Choose numbers that add up to a specific number (with a twist)
我想在 Prolog 的帮助下解决冒险游戏 'Book of Unwritten Tales 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
这是不正确的,因为在分组 #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
感谢任何帮助!
我会采取不同的方法。首先,我会选择一种更易于操作的数据表示形式。每对数字与操作:
[(65,(+)), (17,(-)), (37, (+))]
[(50, (-)), (23, (-)), (27, (+))]
[33, 47, 45]
在这里,number/operation 对是 (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 个列表中的每一个列表中按顺序选择一个项目的列表,使得该列表的评估为 41。
然后解决方案将遍历列表列表的每个元素,select 每个元素的一个成员,评估该 selection 的结果,并将该结果与 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
一个 'declarative' 这个漂亮谜题的解决方案(或者是一个丑陋的 hack?)
:- 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].
由于@CapelliC 的解决方案并不是一个丑陋的 hack,而且他和@lurker 的解决方案都足够优雅和有用,我想我会冒昧地用一个真正丑陋的 hack 来回答。
这肯定不是解决问题的好方法。但它确实有效!
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.
我想在 Prolog 的帮助下解决冒险游戏 'Book of Unwritten Tales 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
这是不正确的,因为在分组 #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
感谢任何帮助!
我会采取不同的方法。首先,我会选择一种更易于操作的数据表示形式。每对数字与操作:
[(65,(+)), (17,(-)), (37, (+))]
[(50, (-)), (23, (-)), (27, (+))]
[33, 47, 45]
在这里,number/operation 对是 (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 个列表中的每一个列表中按顺序选择一个项目的列表,使得该列表的评估为 41。
然后解决方案将遍历列表列表的每个元素,select 每个元素的一个成员,评估该 selection 的结果,并将该结果与 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
一个 'declarative' 这个漂亮谜题的解决方案(或者是一个丑陋的 hack?)
:- 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].
由于@CapelliC 的解决方案并不是一个丑陋的 hack,而且他和@lurker 的解决方案都足够优雅和有用,我想我会冒昧地用一个真正丑陋的 hack 来回答。
这肯定不是解决问题的好方法。但它确实有效!
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.