多項式を前置表現のまま正準表現に直す規則は複雑になる。 そこで多項式のみを表現する場合は、次のように連想リストを用いると処理が少し楽になる。 連想リスト構成要素のコンスセルは加算(+)で結合する。 連想リストの要素のコンスセルの car はキーと呼ばれ、 多項式表現では指数部の数字 n を格納することにする。 そして、 cdr には x^n の係数を格納する。 ・・・しかし、実際のプログラムでは処理の統一性のためにコンスセル ではなく、要素数 2 のリストで表現する。 連想リスト表現は前置表現よりも表現力に乏しいが、多項式の操作をするならば 十分であり、処理が楽になるというメリットがある。
(例)
多項式 | a x^m + b x^n + c |
---|---|
連想リスト表現 | ((m . a) (n . b) (0 . c)) |
表1 に前置表現で表された係数が 1 の単純な多項式を連想リスト表現に直す規則を示す。 係数が 1 でない場合は、乗算演算子(*)で表現されているので、 その処理は多項式の積とみなし、次に説明する多項式の乗算処理規則を利用して処理する。 表1-1,2,3 のように () を一つ余分に付けてコンスセルのリスト(要素数が1の連想リスト) にしているのは、後で append 関数でリストを結合していくためである。
変換規則 | |
---|---|
1 | x → ((1 . 1)) |
2 | atom → ((0 . atom)) |
3 | (^ x n) → ((n . 1)) |
4 | (+ a b) → ((alist a) (alist b)) |
表1: 展開された多項式の前置表現から連想リスト表現への変換
表2 に多項式の乗算を処理する規則を示す。 規則1 で左だけにコンスセルが残るようにし、 規則2 で全てをコンスセルの積に分解する。 そして、規則3 でコンスセル同士の乗算を行い、簡単化する。
変換規則 | |
---|---|
1 | p=p1+p2+...+pn, ただし、 pi は x^i に対応するコンスセル p * q → (+ (* p1 q) (* p2 q) ... (* pn q)) |
2 | q=q1+q2+...+qn, ただし、 pi,qi は x^i に対応するコンスセル pi * q → (+ (* pi q1) (* pi q2) ... (* pi qn)) |
3 | pi,qi は x^i に対応するコンスセル pi * qj → (i+j . (* (pi の係数) (qj の係数))) |
表2: 連想リスト表現された多項式の乗算
次に表3 に示す規則で冪乗を処理する。
変換規則 | |
---|---|
1 | p ^ 1 → p |
2 | p はアトムでないとき p ^ 正整数n → p * p^(n-1) |
表3: 連想リスト表現された多項式の冪乗
関数 | 機能 |
---|---|
(pre2alist expr var) | var の多項式の前置表現 expr を連想リスト表現にする |
(alist2pre expr var) | 多項式の連想リスト表現 expr を var の多項式の前置表現にする |
プログラムリスト: polyalist.el
;;;--------------------------------------------------------------- ;;; 多項式の表現変換 (by 平野 拓一) ;;;--------------------------------------------------------------- ;;;--------------------------------------------------------------- ;;; 必要ファイル: in2pre.el, pre2in.el, derive.el, simplify.el ;;;--------------------------------------------------------------- ;;; リストを1レベル平にする (defun flatten1 (lis) (cond ; nil のとき ((null lis) nil) ; アトムのとき ; 次に nconc で要素連結するために list にしておく ((atom lis) (list lis)) ; lis の先頭要素がアトムのとき ((atom (car lis)) (nconc (list (car lis)) (flatten1 (cdr lis)))) ; それ以外のとき (t (nconc (car lis) (flatten1 (cdr lis)))) ) ) ;;;--------------------------------------------------------------- ;;; 多項式の表現変換をする ;;;--------------------------------------------------------------- ;----------------------------------------------------------------- ; 展開された多項式の前置表現を連想リスト表現に変換する ; ; [Input] ; expr: 前置表現 ; var: 多項式の文字 ; ; [Return] ; 多項式の連想リスト表現 ;----------------------------------------------------------------- (defun pre2alist (expr var) (cond ; var のとき ; x → ((1 . 1)) ((equal expr var) (list (list 1 1))) ; var でないアトムのとき ((atom expr) (list (list 0 expr))) ; + 演算子の処理 ((equal (car expr) '+) (sort_collect (pre2alist_plus expr var))) ; * 演算子の処理 ((equal (car expr) '*) (sort_collect (pre2alist_mult expr var))) ; ^ 演算子の処理 ((equal (car expr) '^) (sort_collect (pre2alist_pow expr var))) ) ) (defun pre2alist_plus (expr var) (cond ; (+ a b) → ((alist a) (alist b)) ; a=(cadr expr) ; b=(caddr expr) (t (append (pre2alist (cadr expr) var) (pre2alist (caddr expr) var))) ) ) ; p,q は多項式の連想リスト表現 (defun alist_mult (p q) (cond ; pi * qj → (i+j . (+ (pi の係数) (qj の係数))) ; pi=(cadr expr) ; qj=(caddr expr) ((and (eq (length p) 1) (eq (length q) 1)) (list (list (simplify (list '+ (caar p) (caar q))) (simplify (list '* (cdar p) (cdar q)))))) ; pi * q → (+ (* pi q1) (* pi q2) ... (* pi qn)) ((eq (length p) 1) (mapcar '(lambda (qj) (car (alist_mult p (list qj)))) q)) ; p * qj → (+ (* p1 qj) (* p2 qj) ... (* pn qj)) ((eq (length q) 1) (mapcar '(lambda (pi) (car (alist_mult (list pi) q))) p)) ; p * q → (+ (* p1 q) (* p2 q) ... (* pn q)) (t (flatten1 (mapcar '(lambda (pi) (alist_mult (list pi) q)) p))) ) ) ; * 演算子の処理 (defun pre2alist_mult (expr var) (cond ; (* a b) ; a=(cadr expr) ; b=(caddr expr) (t (alist_mult (pre2alist (cadr expr) var) (pre2alist (caddr expr) var))) ) ) (defun pre2alist_pow (expr var) (cond ; (^ x n) → ((n . 1)) ; ; (^ a b) ; a=(cadr expr) ; b=(caddr expr) ((and (equal (cadr expr) var) (integerp (caddr expr))) (list (list (caddr expr) 1))) ; (^ p 1) → p ; ; (^ a b) ; a=(cadr expr) ; b=(caddr expr) ((eq (caddr expr) 1) (pre2alist (cadr expr) var)) ; (^ p n) → (* p (^ p (- n 1))) ; ; (^ a b) ; a=(cadr expr) ; b=(caddr expr) ((and (> (length (cadr expr)) 1) (natnump (caddr expr))) (alist_mult (pre2alist (cadr expr) var) (pre2alist (list '^ (cadr expr) (simplify (list '- (caddr expr) 1))) var))) ; その他の場合 (t expr) ) ) ;----------------------------------------------------------------- ; ソートして x^n の項はまとめる ; ; [Input] ; expr: 連想リスト表現 ;----------------------------------------------------------------- (defun sort_collect (expr) (let ((ord_prev -1) (p (sort expr 'sort_down_ord)) (expr2 ())) (while (not (null p)) (if (equal (caar p) ord_prev) ;---- true: 前と指数部が同じとき (setq expr2 (append (list (list ord_prev ; 係数部の表現は簡単化しておく (simplify (list '+ (cdar expr2) (cdar p))))) (cdr expr2))) ;---- false: 違う時 (setq expr2 (append (list (car p)) expr2)) ) (setq ord_prev (caar p)) ; 指数部の値を更新 (setq p (cdr p)) ; 次のコンスセル ) expr2 ) ) ; 降べきの順に並べるための比較関数 (defun sort_down_ord (cons1 cons2) (if (< (car cons1) (car cons2)) t nil ) ) ;----------------------------------------------------------------- ; 多項式の連想リスト表現を前置表現をに変換する ; ; [Input] ; expr: 多項式の連想リスト表現 ; var: 多項式の文字 ; ; [Return] ; 多項式の前置表現 ;----------------------------------------------------------------- (defun alist2pre (expr var) (cond ; 最後の項のとき ; ; ((a b)) ; a=(caar expr) ; b=(car (cdar expr)) ((eq (length expr) 1) (list '* (car (cdar expr)) (list '^ var (caar expr)))) ; 項が2つ以上あるとき (t (list '+ (list '* (car (cdar expr)) (list '^ var (caar expr))) (alist2pre (cdr expr) var))) ) ) ;;;--------------------------------------------------------------- ;;; 確認テスト ;;;--------------------------------------------------------------- ; テスト関数 ;(setq expr (in2pre '((x + a) ^ 2 + x * x))) ;(setq expr (in2pre '(a * x ^ 1 + b + x ^ 3))) (setq expr (in2pre '((a * x + 1) ^ 2 + 1))) ; 多項式の前置表現から連想リスト表現へ (pre2alist expr 'x) ; 多項式の連想リスト表現から前置表現へ (pre2in (simplify (alist2pre (pre2alist expr 'x) 'y))) ;; ;; End of file ;;
入力 | (setq expr (in2pre '((a * x + 1) ^ 2 + 1))) (pre2alist expr 'x) |
---|---|
出力 | ((2 (^ a 2)) (1 (* 2 a)) (0 2)) |