問題2-37

マトリックス(行列)に関する問題です。行列については、遠山啓氏の『数学入門』にちょっとだけ登場した概念で、いまいち理解を深められなかった部分であり、また興味深い部分でもあったわけですが、この問題を解きながら、その興味をより一層深めることができました。しかし、行列の計算については慣れない点が多々あり、今回は本の中で書かれている数式をそのままcodeに書き直すだけで精一杯でした。
まずは、以下のような行列を定義します。これを、それ以降の計算に使います。縦と横の要素数を上手いこと調節しないと、計算時にエラーが発生してしまいます。

(define v (list 2 4 6 8))
(define w (list 1 3 5 7))

(define m (list (list 1 2 3 4)
                (list 5 6 7 8)
                (list 9 10 11 12)))

(define n (list (list 3 4)
                (list 5 6)
                (list 7 8)
                (list 9 10)))

ベクタ同士の内積(総和?)は、本書に書かれている通り、以下のように定義できます。互いのリストの同じ列の要素を掛け合わせて、その結果の総和を返します。

(define (dot-product v w)
  (accumulate + 0 (map * v w)))

(dot-product v w)

=>100

次に、行列と、その行列の列数と等しいベクタを組み合わせる手続きを定義します。行列の各列をmapで抽出するとベクタになりますので、それともう一方のベクタを上記の内積手続きで組み合わせます。結果は、元の行列の列数と等しい要素数のベクタとなります。(図で書いた方がわかりやすいです・・・)

(define (matrix-*-vector m v)
  (map (lambda (vector)
         (dot-product v vector))
       m))

(matrix-*-vector m v)

=>(60 140 220)

今度は、行列の行と列を反転させる手続きを定義します。一つ前の問題のauumulate-nを利用すると、上手いこと処理を書くことができます。

(define (transpose mat)
  (accumulate-n cons
                '()
                mat))

(transpose m)

=>((1 5 9) (2 6 10) (3 7 11) (4 8 12))

最後は、行列同士を組み合わせる手続きになります。一方の行数と、もう一方の列数の要素数を一致させる必要があります。これまでに定義してきた、行列*ベクタや、行列の反転を利用して、この手続きを定義します。結果の要素数は、一致していない方の要素同士の行列になります。

(define (matrix-*-matrix m n)
  (let ((cols (transpose n)))
    (map (lambda (vector)
           (matrix-*-vector cols vector))
         m)))

(matrix-*-matrix m n)

=>((70 80) (166 192) (262 304))

mapを使ってリストをシンプルに操作する、ということを体感することができた問題でした。