表达转换问题

时间:2011-04-22 15:57:37

标签: c++ algorithm transform

假设我们有以下声明:s = 3 * a * b - 2 * c,其中sabc是变量。此外,我们使用Shunting Yard算法构建RPN表达式,因此现在我们可以将值分配给变量abc并计算{{1}通过使用简单的RPN评估器来获取值。

但问题是,当设置了所有其他变量的值时,我应该能够计算任何变量sab的值。

所以,我需要以某种方式转换现有表达式以获得一组表达式:

c

如何根据一个原始声明生成此类表达式?有没有解决此类问题的标准方法?

约束:

  1. 一组可用的运算符:a = (s + 2 * c) / (3 * b) b = (s + 2 * c) / (3 * a) c = (3 * a * b - s) / 2 +-*,包括一元/+
  2. 运营商-*/双方都不能拥有相同的变量(例如=s = a * a不可接受)< / LI>

    由于

3 个答案:

答案 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 = xf = g在所有步骤中保存x = g作为解决方案。

在每个步骤中,确保x保留在lhs上,同时将lhs的深度减少一个。因此,该算法将在有限的步骤之后终止。

在你的例子中(解决b):

  1. f = 3a*b*2c*-g = s
  2. f = 3a*b*g = s2c*+
  3. f = bg = s2c*+3a*/
  4. 因此b = (s + 2*c)/(3*a)

    如果你有更多的操作符,你可以扩展算法,但如果它们不可逆,你可能会遇到问题。