表达式树的单变量符号微分

问题描述

我正在尝试寻找最佳方法/算法,以符号方式计算 Javascript 中类型化数学函数的导数。每个方程将只有一个变量。 我从一个类型表达式开始,然后使用 Dijkstra 的 RPN 将中缀方程转换为后缀方程,然后将其转换为 shunting-yard algorithm(反向波兰符号)。我的下一个想法是使用它来创建一个表达式树,它最终看起来像这样:

Equation: 25x^2 + 61x + 3

rpn: 25 x 2 ^ * 61 x * + 3 +

表达式树:

Expression tree

我查看了“已解决”表达式树应该是什么样子,以了解需要做什么:

Solved tree

Derivative (without simplification): x^(2-1)*2*25+61

我想我可以使用 postorder traversal 递归应用微分规则(乘积、商、总和、差等)并根据运算符替换“节点”。

然而,在查看导数的表达式树后,我意识到我不能轻易地将乘积规则之类的东西应用到仍然有孩子的节点(即this

所以我的主要问题是:是否有任何已知的算法类似于调车场以编程方式有效地计算符号导数?

解决方法

Shunting Yard 算法更多的是关于解析而不是转换,因此恕我直言,最好遍历结果表达式树,在单独的树中构建派生表达式。对于简单的表达式,就地替换似乎是解决方案,但随着数学表达式变得更加复杂,微分规则(参见 https://en.wikipedia.org/wiki/Differentiation_rules)将使树操作变得复杂,尤其是当算法深入研究子节点进一步区分。例如...

  • 乘积规则是 f(x) g(x) = f'(x) g(x) + f(x) g'(x)

...因此,为了区分以下...

  • x * ln( x )

...结果...

  • ( x )' * ln( x ) + x * ( ln( x ) )'
  • 1 * ln( x ) + x * 1/x

我想您还会发现,一旦创建了导数,就需要对表达式进行一些代数清理。按照示例,该算法将需要第二遍遍历导数树并根据代数规则简化表达式,例如...

  • 1 * n = n
  • n * 1 / n = 1

...所以最后的结果是...

  • ln( x ) + 1

https://www.derivative-calculator.net/ 实际上有一个很好的资源,它是您要在代码中执行的操作的实现,可能值得尝试尝试以了解所涉及的逻辑。此外,Calculate string value in javascript,not using eval 处有一个出色的 Shunting Yard 算法实现。