我收到了一个数字列表,例如[22,45,2,6,7,...]
。
现在我必须插入二元运算符:+
,-
,/
,*
和括号(
,{数字之间的{1}}使得表达式等于给定数字 k 。
列出所有可能的表达式,通过插入运算符和括号来创建,它们将给出 k 的总和。
必须修正数字在结果表达式中的位置,即只在数字之间插入运算符和括号
例如:给定号码)
和列表k=9
,一个解决方案是[1,2,3]
。
我该怎么做?
[我目前的错误解决方案]:
现在我知道如何评估像[(,(,1,+,2,),*,3,)]
这样的表达式,从左到右依次吃Operand1,Operator,Operand2,直到没有东西可吃。
但我也要插入括号..
任何人都可以草拟解决方案或提示吗?
这是一个旧的考试问题,我准备考试将在3个月内完成,所以我试图解决这些问题,但我已经陷入困境。
编辑:这是序言问题。答案 0 :(得分:1)
我认为尝试在遍历输入时使用括号直接构建结果列表是个坏主意。构建表达式的语法树更容易,该表达式的叶子用给定列表的元素标记,然后在单独的步骤中处理它。
例如:
?- leaves_expr([A,B,C,D], Expr).
Expr = leaf(A)+ (leaf(B)+ (leaf(C)+leaf(D))) ;
Expr = leaf(A)+ (leaf(B)+leaf(C)*leaf(D)) ;
Expr = leaf(A)+ (leaf(B)+leaf(C)+leaf(D)) ;
Expr = leaf(A)+ (leaf(B)*leaf(C)+leaf(D)) ;
Expr = leaf(A)+leaf(B)* (leaf(C)+leaf(D)) ;
Expr = leaf(A)+leaf(B)* (leaf(C)*leaf(D)) ;
这可以实现如下:
leaves_expr([X], leaf(X)).
leaves_expr(Leaves, X + Y) :-
append([L|Left], [R|Right], Leaves),
leaves_expr([L|Left], X),
leaves_expr([R|Right], Y).
leaves_expr(Leaves, X * Y) :-
append([L|Left], [R|Right], Leaves),
leaves_expr([L|Left], X),
leaves_expr([R|Right], Y).
append/3
调用用于将叶子列表分解为非空部分,以避免不确定问题。 我会对使用DCG这样做的优雅方式感兴趣。
然后,给定这样的表达式树,我们可以用完全括号的形式再次“输出”它:
expr_parenthesized(leaf(X)) -->
[X].
expr_parenthesized(X + Y) -->
['('],
expr_parenthesized(X),
[+],
expr_parenthesized(Y),
[')'].
expr_parenthesized(X * Y) -->
['('],
expr_parenthesized(X),
[*],
expr_parenthesized(Y),
[')'].
撰写这两部分,我们得到:
?- leaves_expr([A,B,C], Expr), expr_parenthesized(Expr, Parenthesized).
Expr = leaf(A)+ (leaf(B)+leaf(C)),
Parenthesized = ['(', A, +, '(', B, +, C, ')', ')'] ;
Expr = leaf(A)+leaf(B)*leaf(C),
Parenthesized = ['(', A, +, '(', B, *, C, ')', ')'] ;
Expr = leaf(A)+leaf(B)+leaf(C),
Parenthesized = ['(', '(', A, +, B, ')', +, C, ')'] ;
Expr = leaf(A)*leaf(B)+leaf(C),
Parenthesized = ['(', '(', A, *, B, ')', +, C, ')'] ;
Expr = leaf(A)* (leaf(B)+leaf(C)),
Parenthesized = ['(', A, *, '(', B, +, C, ')', ')'] ;
Expr = leaf(A)* (leaf(B)*leaf(C)),
Parenthesized = ['(', A, *, '(', B, *, C, ')', ')'] ;
等等。如果你编写简单谓词expr_value/2
来评估这些表达式(从叶子上的数字构造),你就完成了。
答案 1 :(得分:1)
在没有实际放置任何括号的情况下考虑括号问题的一种方法是使用后缀表示法。换句话说:
(a + b) * c
变成:
a b + c *
这是规范Prolog表示法中的以下树:
*(+(a, b), c)
类似地:
a + (b * c) ---> a b c * + ---> +(a, *(b, c))
有一个完整的例子,有三个操作数,1,2和3,只有+
和*
作为运算符,为了简短起见,你得到:
1 2 + 3 + ---> (1 + 2) + 3 = 6
1 2 + 3 * ---> (1 + 2) * 3 = 9
1 2 * 3 + ---> (1 * 2) + 3 = 6
1 2 * 3 * ---> (1 * 2) * 3 = 6
1 2 3 + + ---> 1 + (2 + 3) = 6
1 2 3 + * ---> 1 * (2 + 3) = 5
1 2 3 * + ---> 1 + (2 * 3) = 7
1 2 3 * * ---> 1 * (2 * 3) = 6
查看第一列,我得到以下一般想法:您从 n 操作数和 n -1二元运算符开始。您在堆栈上推送前两个操作数,并需要执行2 * n -3步骤。在每个步骤中,您可以按操作数或应用运算符。如果你还剩下任何一个,你总是可以推动一个操作数。只有在堆栈中有两个或更多操作数时,才能应用运算符;你必须在那时减少堆栈。
回溯将负责列举所有可能性(因此这是解决方案空间的典型蛮力穷举搜索)。您将有两个选择点来源:选择一个运算符;并推或减。
考虑到这一点,我得到了一个谓词的以下实现,它带有一个操作数列表,一个二元运算符列表,并给你一个“带括号”的表达式:
expr(Operands, Operators, E) :-
Operands = [A, B|Rest],
length(Operands, N),
Steps is 2*N - 3,
expr(Steps, Rest, [B, A], Operators, E).
这会将前两个操作数推送到堆栈并计算剩余的步数。
expr(Steps, Operands, Stack, Operators, E) :-
( succ(Steps0, Steps) ->
next(Steps0, Operands, Stack, Operators, E)
; Stack = [E]
).
在这里,我使用succ/2
倒数到0,然后停止;最后,堆栈中唯一的元素就是你的表达。
next(Steps, Operands, Stack, Operators, E) :-
push(Operands, Stack, Operands_next, Stack_next),
expr(Steps, Operands_next, Stack_next, Operators, E).
next(Steps, Operands, Stack, Operators, E) :-
member(Op, Operators),
reduce(Stack, Op, Stack_next),
expr(Steps, Operands, Stack_next, Operators, E).
这是您推或减的地方。这两个单独的条款是选择点的第一个来源;使用member/2
从列表中取一个运算符是另一个。
push([X|Xs], S0, Xs, [X|S0]).
reduce([A,B|Stack], Op, [X|Stack]) :-
X =.. [Op, B, A].
实施推动和减少是微不足道的。我使用“univ”运算符=..
从+(1, 2)
等列表中创建[+, 1, 2]
等字词。
有了这个,你可以问“我如何使用+,*和括号来制作[1,2,3]中的7个”:
?- expr([1,2,3], [+,*], E), E =:= 7.
E = 1+2*3 ;
false.
这是最基本的“生成和测试”:您生成算术表达式,然后测试它们是否计算为值。如果省略测试,则可以看到所有表达式:
?- expr([1,2,3], [+,*], E).
E = 1+(2+3) ;
E = 1*(2+3) ;
E = 1+2*3 ;
E = 1*(2*3) ;
E = 1+2+3 ;
E = (1+2)*3 ;
E = 1*2+3 ;
E = 1*2*3 ;
false.
一个奇怪的细节是因为+
和*
已经被定义为中缀运算符,Prolog会编写它们,甚至为它们括起来。我不知道像E = (1+2)*3
这样的解决方案对你来说是否足够好,或者你真的需要['(', 1, +, 2, ')', *, 3]
。 The other answer似乎已经为此提供了有效的解决方案。由于此处的表达式已经是一个有效的算术表达式,因此您必须稍微调整它。我可能会这样写:
infix(N) -->
{ number(N)
}, !,
[N].
infix(E) -->
{ compound(E),
E =.. [Op, A, B]
}, !,
['('], infix(A), [Op], infix(B), [')'].
我也不知道1 + 2 + 3 = 3 + 3 = 6是否与1+(2 + 3)= 1 + 5 = 6相同:你需要考虑关联性吗?
无论哪种方式,您都可以将expr/3
包装在这样的谓词中:
equals_k(Numbers, K, E) :-
expr(Numbers, [+,-,*,/], E0),
K =:= E0,
phrase(infix(E0), E).
PS:很容易得到除零异常,例如:
?- expr([1,0], [/], E), R is E.
答案 2 :(得分:0)
这是我的解决方案建议,我认为它很简单明了, 复制并粘贴到notepad ++编辑器以获得最佳可读性。
* ________________________________________________ *
*|find_expression(NumsList,TargetValue,Expression)| *
**------------------------------------------------* *
* Expression is an arithmetic expression of the numbers in Numslist with *
* possible operators '+','-','*','/' and '(' and ')' between the numbers *
* in such a way that the expression evaluates to the TargetValue argument *
*****************************************************************************/%
/* a single element number list can evaluate only to itself */
find_expression([SingleNumber],SingleNumber,SingleNumber).
/* expression of a multypile number list */
find_expression(NumberList,Target,Expression):-
/* non-deterministically divide the number list
into 2 separate lists which include at least one number each*/
append([X|Xs],[Y|Ys], NumberList),
/* recursively find an expression for east list,
where the expression evaluates to itself */
find_expression([X|Xs],Exp1,Exp1),
find_expression([Y|Ys],Exp2,Exp2),
/* non-deterministically choose an operand from [+,-,*,division]
and compose Expression to be (Exp1 Operand Exp2) */
( member(Expression,[Exp1+Exp2,Exp1-Exp2,Exp1*Exp2])
; /* prevent zero divison */
(Val2 is Exp2, Val2 =\= 0, Expression = (Exp1/Exp2))), %/*
/* assure that final expression evaluates(matches) the targe value
and convert value from integer to float if necessary */
( Target = Expression ; Target is Expression
; FloatTarget is Target*1.0, FloatTarget is Expression)