作成日記 Schemeで数値計算(1)

線形代数周辺の問題を解けるようにすることは、私の大きな目標の1つである。数値計算をする上で、線形代数、行列計算は避けて通れない。

まず、行列計算の本当に基本的な部分をlist操作を使って書く。Gaucheにはarrayもあるのは知っているが、あえて使わない方針をとる。

Schemeらしく、高階関数やlambda,mapを使い、短く書くことに努める。
mapとほぼ同じであるが、my-map-item2を自作した。これは引数の2つの行列のサイズが異なるときに、エラーを吐かせるためである。

行列の転置 transposed-mat、和 add-mat、スカラー倍 scalar-mat、積 mul-mat、減 sub-mat、単位行列eye-mat を定義する。
col1sep-matは行列の1列目とそれ以外を分離する関数である。col2lineは1列を1行に変換する。col1sep-matとcol2lineを使って行列の転置を定義した。

(define (my-map-item2 op a b)
  (define (my-map-item2-pre op a b tmp)
    (if (and (null? a) (null? b))
	tmp
	(my-map-item2-pre op
			  (cdr a)
			  (cdr b)
			  (append tmp (list (op (car a) (car b)))))))
  (my-map-item2-pre op a b '() ))

(define (add-mat A1 A2)
  (define (add-each a1 a2) (my-map-item2 + a1 a2))
  (my-map-item2 add-each A1 A2))

(define (scalar-mat a A)
  (define (scalar-each a a1) (map (lambda (x) (* a x)) a1))
  (map (lambda (x) (scalar-each a x)) A))

(define (sub-mat A1 A2) (add-mat A1 (scalar-mat -1.0 A2)))

(define (l-accum2 op1 op2 ini a b)
  (if (and (null? a) (null? b))
      ini
      (l-accum2 op1 op2 (op1 ini
			     (op2 (car a) (car b)))
		(cdr a) (cdr b))))

(define (sum-of-mul-each a1 a2) (l-accum2 + * 0 a1 a2))

(define (col1sep-mat A)
  (define (col1sep-mat-pre  A tmp1 tmp2)
    (if (null? A)
	(cons tmp1 tmp2)
	(col1sep-mat-pre (cdr A)
				(append tmp1 (list (list (caar A))))
				(append tmp2 (list (cdr (car A)))))))
  (col1sep-mat-pre A '() '() ))

(define (col2line col)
  (define (col2line-p col tmp)
    (if (null? col)
	tmp
	(col2line-p (cdr col)
		    (append tmp (list (caar col))))))
  (col2line-p col '() ))

(define (transposed-mat A)
  (define (transposed-mat-p A tmp)
    (if (null? (car A))
	tmp
	(transposed-mat-p (cdr (col1sep-mat A))
			  (append tmp
				  (list (col2line (car (col1sep-mat A))))))))
  (transposed-mat-p A '() ))

(define (mul-oneline-mat oneline A2)
  (map (lambda (x) (sum-of-mul-each oneline x)) (transposed-mat A2)))

(define (mul-mat A1 A2)
  (define (mul-mat-pre A1 A2 tmp)
    (if (null? A1)
	tmp
	(mul-mat-pre (cdr A1) A2
		     (append tmp
			     (list (mul-oneline-mat (car A1) A2))))))
  (mul-mat-pre A1 A2 '() ))

(define (eye-line len loc1)
  (define (eye-line-p len loc1 tmp)
    (if (<= len 0)
	tmp
	(eye-line-p (- len 1)
		    (- loc1 1)
		    (append tmp (list (if (= loc1 1) 1.0 0))))))
  (eye-line-p len loc1 '() ))

(define (eye-mat N)
  (define (eye-mat-pre N i tmp)
    (if (> i N)
	tmp
	(eye-mat-pre N (+ i 1) (append tmp (list (eye-line N i))))))
  (eye-mat-pre N 1 '()))


動作確認

(define A1 '( (1 2 3)
	      (4 5 6)))

;;A1
;;=> ((1 2 3) (4 5 6))


(define tA1 (transposed-mat A1))

;;tA1
;;=> ((1 4) (2 5) (3 6))


(define eye3 (eye-mat 3))

;;eye3
;;=> ((1.0 0 0) (0 1.0 0) (0 0 1.0))


;;(add-mat A1 A1)
;;=> ((2 4 6) (8 10 12))

;;(scalar-mat 3 A1)
;;=> ((3 6 9) (12 15 18))


;;(mul-mat A1 (eye-mat 3))
;;=>((1.0 2.0 3.0) (4.0 5.0 6.0))

;;(mul-mat A1 tA1)
;;=>((14 32) (32 77))


つづく