逆行列の計算
必要になって何となく OCaml で書いて何となく晒す。
なんとなく面倒臭そうなイメージを持ってたんですが,実際に書いてみたらそうでもありませんでした。
破壊的代入の塊であるという点では関数型らしくありませんが,配列の破壊的操作が Array.iteri で思いの外すっきり書けたあたりは関数型言語の特性かなあ,と思いました。
let init_matrix m n f = Array.init m (fun i -> Array.init n (fun j -> f i j)) let make_unit_matrix n = init_matrix n n (fun i j -> if i = j then 1.0 else 0.0) (* destructive version of Array.map; works as (map-into a f a) in CL *) let amap f a = Array.iteri (fun i x -> a.(i) <- f x) a (* another variant: (map-into a f a b) *) let amap2 f a b = Array.iteri (fun i x -> a.(i) <- f x b.(i)) a (* some destructive operations on rows *) let swap_rows m i j = let ri, rj = m.(i), m.(j) in m.(i) <- rj; m.(j) <- ri let subtract_row m i j x = amap2 (fun a b -> a -. b *. x) m.(i) m.(j) let magnify_row m i x = amap (( *. ) x) m.(i) (* sweeping algorithm *) let rec find_pivot m i' i = if m.(i').(i) <> 0.0 then i' else find_pivot m (i' + 1) i let sweep_ m i = (let i' = find_pivot m i i in swap_rows m i i'); magnify_row m i (1.0 /. m.(i).(i)); for i' = 0 to Array.length m - 1 do if i <> i' then subtract_row m i' i (m.(i').(i)) done let sweep m = for i = 0 to Array.length m - 1 do sweep_ m i done; m (* ad-hoc stuff *) let concatenate_matrix m1 m2 = Array.init (Array.length m1) (fun i -> Array.append (m1.(i)) (m2.(i))) let extract_rhs m = let n = Array.length m in Array.init n (fun i -> Array.sub (m.(i)) n n) (* main function *) let inverse_matrix m = let e = make_unit_matrix (Array.length m) in let m' = concatenate_matrix m e in extract_rhs (sweep m')