5章 記号微分

この章では記号微分を扱う。 表1,2 の微分公式をサポートする記号微分プログラムを完成させる。

微分公式1

表1: 微分公式1

微分公式2

表2: 微分公式2

微分とか積分の計算していると何となくインテリジェントな気がするものである。 しかし、インテリジェントな香りのする微分・積分も実は与えられた式に合った公式をテーブル から引いて形を変形しているだけなのである。 だから、微分・積分の意味のわからない人をアルバイトに雇って式を変形させることもできる。 このように方法が決まっている単純作業だったら容易にプログラムでインプリメント可能である。 ただし、数式の表現は無限に括弧を使って入れ子構造にできるため、再帰的に処理を行う。 再帰呼出しのプログラムを組むには慣れがいるが、数式の表現としては 4 章で説明した 前置表現を用いているので、非常に楽である。なぜならば、前置表現では二分木構造の ノードに当たる演算子が先頭に書かれていてその演算子を見るだけで表1,2 のどの公式を 適用すればよいかわかるからである。

記号微分を行う Lisp プログラム

関数 機能
(derive expr var) 前置表現 expr を var で微分する

記号微分のメイン関数は derive である。引数の expr には前置表現された 数式を与え、 var に微分する変数を与える。 表1-5 の合成関数の微分の公式は表2の個々の公式で行われるので、 わざと適用する必要はない。 また表1-1の公式は表1-3の特殊なケースと見なせるので、実際にプログラムで 使うのは表1では2,3,4の公式である。
表2の公式は全て用いる。実際には表2-1,2 の公式は表2-3の特殊な場合であるが、 後の場合分けが面倒なので、 dependq, freeq という変数に依存しているかどうか を再帰的に調べる関数を使って、冪関数、指数関数、一般の冪関数は別のもの として処理する。

プログラムリスト: derive.el


;;;---------------------------------------------------------------
;;; 記号微分 (by 平野 拓一)
;;;---------------------------------------------------------------

;;;---------------------------------------------------------------
;;; 必要ファイル: in2pre.el, pre2in.el
;;;---------------------------------------------------------------

;;;---------------------------------------------------------------
;;; 再帰的に変数依存性を調べる関数
;;;---------------------------------------------------------------

; f は x に依存しているかどうかを調べる
(defun dependq (f x)
    (cond
        ;---- atom
        ((atom f) (if (equal f x) t nil))
        ;---- 関数の処理
        ((eq (length f) 2) (dependq (cadr f) x))
        ;---- 四則演算と冪乗の処理
        ((eq (length f) 3) (or (dependq (cadr f) x)
                               (dependq (caddr f) x)))
        ;---- それ以外の場合
        (t nil)
    )
)

; f は x に依存していないかどうかを調べる
(defun freeq (f x)
    (not (dependq f x))
)

;;;---------------------------------------------------------------
;;; 記号微分する
;;;---------------------------------------------------------------

(defun derive (expr var)
    (cond
        ;---- アトムのときの処理
        ((atom expr) (if (equal expr var) 1 0))
        ;---- 冪関数と指数関数の微分
        ((equal (car expr) '^) (derive_pow_exp expr var))
        ;---- 掛け算の微分
        ((equal (car expr) '*) (derive_mult expr var))
        ;---- 割り算の微分
        ((equal (car expr) '/) (derive_div expr var))
        ;---- 和と差の微分
        ((or (equal (car expr) '+) (equal (car expr) '-))
         (derive_pm expr var))
        ;---- sin の微分
        ((equal (car expr) 'sin) (derive_sin expr var))
        ;---- cos の微分
        ((equal (car expr) 'cos) (derive_cos expr var))
        ;---- exp の微分
        ((equal (car expr) 'exp) (derive_exp expr var))
        ;---- log の微分
        ((equal (car expr) 'log) (derive_log expr var))
        ;---- 多重括弧を外す
        ((not (null expr)) (derive (car expr) var))
        ;---- その他の場合
        (t nil)
    )
)

; 冪関数と指数関数の微分
(defun derive_pow_exp (expr var)
    (cond
        ;---- 冪関数 (f(x)^a)'=a*(f(x)^(a-1))*f'(x)
        ;
        ; f(x)=(cadr expr)
        ; a=(caddr expr)
        ((and (dependq expr var) (freeq (caddr expr) var))
         (list '* (derive (cadr expr) var)
                  (list '* (caddr expr)
                           (list '^ (cadr expr)
                           (list '- (caddr expr) 1)))))
        ;---- 指数関数 (a^f(x))'=(a^f(x))*log(a)*f'(x)
        ;
        ; a=(cadr expr)
        ; f(x)=(caddr expr)
        ((and (dependq expr var) (freeq (cadr expr) var))
         (list '* (derive (caddr expr) var)
                  (list '* (cons (car expr) (cdr expr))
                           (cons 'log (cadr expr)))))
        ;---- 一般の冪乗
        ; (f(x)^g(x))'=(f(x)^g(x))*( (f'(x)*g(x))/f(x)+log(f(x))*g'(x) )
        ; 証明は f^g = h とおいて log を取る
        ;
        ; f(x)=(cadr expr)
        ; g(x)=(caddr expr)
        (t
         (list '* (cons (car expr) (cdr expr))
                  (list '+ (list '/ (list '* (derive (cadr expr) var)
                                             (caddr expr))
                                    (cadr expr))
                           (list '* (cons 'log (cadr expr))
                                    (derive (caddr expr) var)))))
   )
)

; 掛け算の微分公式
; (f*g)'=f'*g+f*g'
;
; f(x)=(cadr expr)
; g(x)=(caddr expr)
(defun derive_mult (expr var)
    (list '+ (list '* (derive (cadr expr) var) (caddr expr))
             (list '* (cadr expr) (derive (caddr expr) var)))
)

; 割り算の微分公式
; (f/g)'=(f'*g-f*g')/(g^2)
;
; f(x)=(cadr expr)
; g(x)=(caddr expr)
(defun derive_div (expr var)
    (list '/ (list '- (list '* (derive (cadr expr) var) (caddr expr))
                      (list '* (cadr expr) (derive (caddr expr) var)))
             (list '^ (caddr expr) 2))
)

; 和と差の微分公式
; (f+g)'=f'+g'
; (f-g)'=f'-g'
;
; f(x)=(cadr expr)
; g(x)=(caddr expr)
(defun derive_pm (expr var)
    (list (car expr) (derive (cadr expr) var)
                     (derive (caddr expr) var))
)

; sin の微分
; sin'(f)=f'*cos(f)
;
; f(x)=(cdr expr)
(defun derive_sin (expr var)
    (list '* (derive (cdr expr) var)
             (cons 'cos (cdr expr)))
)

; cos の微分
; cos'(f)=-f'*sin(f)
;
; f(x)=(cdr expr)
(defun derive_cos (expr var)
    (list '* (derive (cdr expr) var)
             (list '- '0 (cons 'sin (cdr expr))))
)

; exp の微分
; exp'(f)=f'*exp(f)
;
; f(x)=(cdr expr)
(defun derive_exp (expr var)
    (list '* (derive (cdr expr) var)
             (cons (car expr) (cdr expr)))
)

; log の微分
; log'(f)=f'*(1/f)
;
; f(x)=(cdr expr)
(defun derive_log (expr var)
    (list '* (derive (cdr expr) var)
             (list '/ 1 (cadr expr)))
)

;;;---------------------------------------------------------------
;;; 確認テスト
;;;---------------------------------------------------------------

; 微分する関数
;(setq expr '(cos ( 2 * exp ( x ))))
;(setq expr '( x ^ 2 + x + 1 ))
(setq expr '(log( x ^ 3 + 1 ) + x + 1))

; 前置表現に変換
(setq expr (in2pre expr))

; 微分する
(setq expr2 (derive expr 'x))

; 内挿表現に変換
(pre2in expr2)

;;
;; End of file
;;

実行例

入力 (setq expr '(log( x ^ 3 + 1 ) + x + 1))
(setq expr (in2pre expr))
(setq expr2 (derive expr 'x))
(pre2in expr2)
出力 ((1 * 3 * x ^ (3 - 1) + 0) * 1 / (x ^ 3 + 1) + 1 + 0)

Go Back