递归微分基本表达式
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" 是一个复杂的话题。
如果我有一个表达式,例如 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" 是一个复杂的话题。