假设我们有以下声明:s = 3 * a * b - 2 * c
,其中s
,a
,b
和c
是变量。此外,我们使用Shunting Yard算法构建RPN表达式,因此现在我们可以将值分配给变量a
,b
和c
并计算{{1}通过使用简单的RPN评估器来获取值。
但问题是,当设置了所有其他变量的值时,我应该能够计算任何变量s
,a
或b
的值。
所以,我需要以某种方式转换现有表达式以获得一组表达式:
c
如何根据一个原始声明生成此类表达式?有没有解决此类问题的标准方法?
约束:
a = (s + 2 * c) / (3 * b)
b = (s + 2 * c) / (3 * a)
c = (3 * a * b - s) / 2
,+
,-
,*
,包括一元/
和+
-
,*
和/
双方都不能拥有相同的变量(例如=
或s = a * a
不可接受)< / LI>
醇>
由于
答案 0 :(得分:3)
首先看到:Postfix notation to expression tree将您的RPN转换为树。
获得等式left expression = right expression
后,将其更改为left expression - right expression = 0
,并通过调车场和上述答案创建left expression - right expression
树。因此,当您评估树时,必须将答案设为0。
现在根据您的限制,观察如果变量(比如说x)未知,结果表达式将始终为
形式 (ax + b)/(cx + d)
其中a,b,c,d将取决于其他变量。
您现在可以递归地将表达式计算为元组(a,b,c,d)。
最后,您将最终解决线性方程
(ax + b)/(cx + d) = 0
提供x = -b/a
这样您就不必为每个变量计算单独的表达式。一个表达式树就足够了。在给定其他变量的情况下,您只需递归计算元组(a,b,c,d)并最终求解线性方程。
(不完整的)伪代码将是
TupleOrValue Eval (Tree t) {
if (!t.ContainsVariable) {
blah;
return value;
}
Tuple result;
if (t.Left.ContainsVariable) {
result = Eval(t.Left);
value = Eval(t.Right);
return Compose(t.Operator, result, value);
} else {
result = Eval(t.Right);
value = Eval(t.Left);
return Compose(t.Operator, result, value);
}
}
Tuple Compose(Operator op, Tuple t, Value v) {
switch (op) {
case 'PLUS': return new Tuple(t.a + v*t.c, t.b + v*t.d, t.c, t.d);
// (ax+b)/(cx+d) + v = ( (a + vc)x + b + dv)/(cx + d)
// blah
}
}
例如,如果表达式为x+y-z = 0
。树将是
+
/ \
x -
/ \
y z
对于y = 5且z = 2。
Eval(t.Right)将返回y-z = 3,因为该子树不包含x。
Eval(t.Left)将返回(1,0,0,1)
,对应(1x + 0)/(0x + 1)
。注意:上面的伪代码不完整。
现在使用值为3的(1,0,0,1)Compose将给出(1 + 3*0, 0 + 3*1, 0, 1) = (1,3,0,1)
,其对应于(x + 3)/(0x + 1)
。
现在,如果要解决这个问题,请将x设为-b/a = -3/1 = -3
我会留下原来的答案:
一般来说,这是不可能的。
例如考虑表达式
x*x*x*x*x + a*x*x*x*x + b*x*x*x + c*x*x + d*x = e
获取x
的表达式基本上对应于找到多项式的根
x 5 + ax 4 + bx 3 + cx 2 + dx -e
如果你想使用+, - ,/,*和第n个根,那么一般被证明是不可能的。请参阅Abel Ruffini定理。
您是否忘记提及某些限制,这可能会简化问题?
答案 1 :(得分:1)
基本答案是你必须将代数应用于你拥有的方程组,以产生你想要的方程式。
一般来说,如果你从这个符号等式开始:
s = 3 * a * b - 2 * c
并为s,a和c添加约束:
s = ...
a = ...
c = ...
你需要运用代数的标准定律来重新排列方程的集以产生你想要的东西,在这种情况下,是b的公式:
b = ...
如果添加不同的约束,则需要使用相同的代数定律,以不同的方式应用。
如果你的方程是
的所有形式(如你的例子所示)left_hand_side_variable_n = combination_of_variables
然后您可以使用规则来求解联立方程。对于线性组合,这非常简单(你学会了如何做这个高中)。您甚至可以设置标准矩阵并解决using a standard solver package without doing algebra。
如果方程不是线性的,那么无论你的数学有多好,你都可能无法找到解决方案(参见其他答案的例子)。在可能的范围内,您可以使用computer algebra system (CAS)来操作公式。他们通过将公式基本上表示为[math]抽象语法树而不是RPN来实现,并应用源到源转换规则(您可以从高中称这些“代数规则”)。它们通常具有相当大的内置规则(“知识”)。一些CAS将尝试使用内置规则为您解决此类方程组;其他人,你必须告诉什么顺序的代数法应用于什么顺序。
您也可以使用constraint solver,这是一种特殊的计算机代数系统,专注于回答您提出的问题,例如“使用这组约束,以及变量的特定值,其他变量的价值是多少?“如果你的方程遵循它们旨在解决的形式,那么这些非常好;否则没有gaurantee但这并不奇怪。
还可以使用程序转换系统来操纵任意“语法树”,因为algrebra树只是语法树的特例。要使用这样的系统,您可以定义要操作的语言(例如,常规代数)以及可以操作它的规则,以及应用规则的顺序。 [老师在你的介绍代数课上为你做了这个,但不是以这种正式方式]这是我的program transformation system manipulating algebra的一个例子。对于方程求解,您需要大致相同的规则,但需要不同的应用顺序。
最后,如果你想在C ++程序中执行此操作,要么必须模拟上述更一般的机制之一(这是一项大量的工作),要么你已经缩小了你愿意解决的问题(例如,“线性组合”),以便您可以利用更简单的算法。
答案 2 :(得分:0)
对于非常基本的表达式(如在您的示例中),有一个非常直接的表达式,其中每个变量大多数发生一次,每个运算符都是二进制。算法主要是你手工做的。
我们要查找的变量在以下行中为x
。将表达式转换为f(...,x,...) == g(...)
形式。变量x
已经在左侧,或者您只需切换两侧。
现在你有两个函数,包括二元运算符到子表达式的应用,即f = o_1(e_1,e_2)
,其中每个e_i
是一个变量,一个数字或另一个函数e_i = o_i(e_j, e_k)
。可以将其视为二进制树表示,其中节点是运算符,叶子是变量或数字。同样适用于g
。
您可以应用以下算法(我们的目标是将树转换为代表表达式x == h(...)
的树:
while f != x:
// note: f is not a variable but x is a subexpression of f
// and thus f has to be of the form binop(e_1, e_2)
if x is within e_1:
case f = e_1 + e_2: // i.e. e_1 + e_2 = g
g <- g - e_2
case f = e_1 - e_2: // i.e. e_1 - e_2 = g
g <- g + e_2
case f = e_1 * e_2: // i.e. e_1 * e_2 = g
g <- g / e_2
case f = e_1 / e_2: // i.e. e_1 / e_2 = g
g <- g * e_2
f <- e_1
else if x is within e_2:
case f = e_1 + e_2: // i.e. e_1 + e_2 = g
g <- g - e_2
case f = e_1 - e_2: // i.e. e_1 - e_2 = g
g <- g + e_2
case f = e_1 * e_2: // i.e. e_1 * e_2 = g
g <- g / e_2
case f = e_1 / e_2: // i.e. e_1 / e_2 = g
g <- g * e_2
f <- e_1
现在我们已f = x
和f = g
在所有步骤中保存x = g
作为解决方案。
在每个步骤中,确保x
保留在lhs上,同时将lhs的深度减少一个。因此,该算法将在有限的步骤之后终止。
在你的例子中(解决b):
f = 3a*b*2c*-
和g = s
f = 3a*b*
和g = s2c*+
f = b
和g = s2c*+3a*/
因此b = (s + 2*c)/(3*a)
。
如果你有更多的操作符,你可以扩展算法,但如果它们不可逆,你可能会遇到问题。