递归微分基本表达式

Recursively differentiating basic expression

如果我有一个表达式,例如 x * x * x,存储在如下数据结构中:mult(var x, mult(var x, var x))

我想实现一个函数来递归地微分方程(所以到 3 * x * x 或 x*x + (x + x)*x 等,不需要简化),任何关于如何成为这个的建议?

你会找到匹配的区分规则并应用它。例如,在这种情况下,我们有规则(其中 A 和 B 代表整个子表达式)

diff(mult(A, B)) -> add(mult(diff(A),B), mult(A, diff(B)))

这条规则的左边与我们设置时的公式匹配

A = var x
B = mult(var x, var x)

所以我们可以将这个规则应用到公式中得到

diff(mult(var x, mult(var x, var x))) ->
  add(mult(diff(var x),mult(var x, var x)), mult(var x, diff(mult(var x, var x))))

现在对剩余的 diff 操作递归执行相同的操作。

您在这里需要的其他规则是:

diff(var x) -> 1

请注意,语言可以带来不同。 Lisp 对这个问题特别好。

(defun d (f x)
  (etypecase f
    (number 0)
    (symbol (if (eq f x) 1 0))
    (list (df (first f) (rest f) x))))

(defun df (op args x)
  (let ((a (first args))
        (b (second args)))
    (case op
      ((+ -) `(,op ,(d a x) ,(d b x)))
      (* `(+ (* ,a ,(d b x)) (* ,(d a x) ,b)))
      (/ `(/ (- (* ,(d a x) ,b) (* ,a ,(d b x))) (^ ,b 2)))
      (^ `(* (* ,b (^ ,a ,(1- b))) ,(d a x)))
      (sin `(* (cos ,a) ,(d a x)))
      (cos `(* (- (sin ,a)) ,(d a x))))))

Lisp 喜欢前缀符号。这相当于表达式的抽象语法树。二元运算看起来像 (op lhs rhs)。所以要区分(3 sin(x^2))^2,

> (d '(^ (* (sin (^ x 2)) 3) 2) 'x)
(* (* 2 (^ (* (SIN (^ X 2)) 3) 1))
 (+ (* (SIN (^ X 2)) 0) (* (* (COS (^ X 2)) (* (* 2 (^ X 1)) 1)) 3)))

这是一个正确的答案,但显然它远非简单的形式。所以下一步是添加一个表达式简化器。有一个很初级的,

> (simplify (d '(^ (* (sin (^ x 2)) 3) 2) 'x))
(* (* 2 (* (SIN (^ X 2)) 3)) (* (* (COS (^ X 2)) (* 2 X)) 3))

使用中缀表示法,这是 2(3 sin(x^2)) (3 cos(x^2) (2x))。显然可以进行更多的简化,但是通过任何有用的定义得到 "most simple" 是一个复杂的话题。