前言
记得之前看tensorflow, numpy等库的api中提供了自动求导的函数, 觉得其中一定是用了某些高深的数值分析算法,但在 sicp书中也给了个简单的求导程序, 本质是基于求导法则的递归性质来实现的, 感觉有点意思记录一下。
我们先来看看求导法则:
1 2 3 4
| dc / dx = 0 dx / dx = 1 d(u + v) / dx = du/dx + dv/dx d(uv)/dx = u(dv/dx) + v(du/dv)
|
可以看到最后两条求导法则满足递归的性质, 求导的运算过程通过分解后会得到越来越小的片段,最终将产生出常量和变量, 他们的导数都是0或者1.
代码实现
首先我们定义谓语(返回值为bool的函数):
1 2 3 4 5 6 7 8 9
| (define (variable? x) (symbol? x)) (define (same-variable? v1 v2) (and (variable? v1) (variable? v2) (eq? v1 v2)))
(define (sum? x) (and (pair? x) (eq? (car x) '+)))
(define (product? x) (and (pair? x) (eq? (car x) '*)))
|
然后我们需要定义出结果的构造方法:
1 2 3 4 5 6 7
| (define (make-sum a1 a2) (list '+ a1 a2)) (define (make-product a1 a2) (list '* a1 a2))
(define (addend s) (cadr s)) (define (augend s) (caddr s)) (define (multiplier p) (cadr p)) (define (multiplicand p) (caddr p))
|
最后实现求导函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| (define (deriv exp var) (cond ((number? exp ) 0) ((variable? exp) (if (same-variable? exp var) 1 0)) ((sum? exp) (make-sum (deriv (addend exp) var) (deriv (augend exp) var))) ((product? exp) (make-sum (make-product (multiplier exp) (deriv (multiplicand exp) var)) (make-product (deriv (multiplier exp) var) (multiplicand exp)) ) ) (else (error "unknown expression type: " exp)) ) )
|
接下来我们可以测试一下:
1 2 3 4
| > (deriv '(* x 3) 'x) (+ (* x 0) (* 1 3)) > (deriv '(+ x y) 'x) (+ 1 0)
|
复杂D都得:
1 2
| > (deriv '(* (* x y) (+ x 3)) 'x) (+ (* (* x y) (+ 1 0)) (* (+ (* x 0) (* 1 y)) (+ x 3)))
|
当然, 这个求导程序还是一个朴素的实现, 还需要对结果进行化简, 但是它已经实现了最核心的部分了…