(* Matrices creuses *)

module M = Map.Make(struct type t = int let compare = compare end)

type t = { cols: int; rows: int M.t array }

let id n =
  { cols = n; rows = Array.init n (fun i -> M.singleton i 1) }

let init n m f =
  let rec row acc i j =
    if j = m then acc else
    row (let v = f i j in if v = 0 then acc else M.add j v acc) i (j + 1) in
  { cols = m; rows = Array.init n (fun i -> row M.empty i 0) }

let size a =
  (Array.length a.rows, a.cols)

let add a b =
  if size a <> size b then invalid_arg "add";
  let add j aij bij = match aij, bij with
    | Some x, Some y -> let z = x + y in if z = 0 then None else Some z
    | (Some _ as x), None | None, (Some _ as x) -> x
    | None, None -> None
  in
  { cols = a.cols;
    rows = Array.mapi (fun i r -> M.merge add r b.rows.(i)) a.rows }

let mul a b =
  let n, p = size a in
  let q, m = size b in
  if q <> p then invalid_arg "mul";
  let rows = Array.make n M.empty in
  let add i j v =
    let cij = v + try M.find j rows.(i) with Not_found -> 0 in
    rows.(i) <- if cij = 0 then M.remove j rows.(i) else M.add j cij rows.(i)
  in
  for i = 0 to n - 1 do
    M.iter
      (fun k aik ->
        M.iter
          (fun j bkj -> add i j (aik * bkj))
          b.rows.(k))
      a.rows.(i)
  done;
  { cols = m; rows = rows }

let rec power x n =
  if n = 0 then
    id x.cols
  else
    let y = power x (n/2) in
    if n mod 2 = 1 then mul x (mul y y) else mul y y

let rec print fmt m =
  Array.iter
    (fun rowi ->
      for j = 0 to m.cols - 1 do
        Format.fprintf fmt "%2d " (try M.find j rowi with Not_found -> 0)
      done;
      Format.fprintf fmt "@\n")
    m.rows

(* tests *)

let rec forall i j p = i >= j || p i && forall (i + 1) j p

let equal m1 m2 =
  m1.cols = m2.cols &&
  Array.length m1.rows = Array.length m2.rows &&
  forall 0 (Array.length m1.rows) (fun i -> M.equal (=) m1.rows.(i) m2.rows.(i))

let () =
  let m = id 17 in
  assert (equal (mul m m) m);
  let m' = init 17 17 (fun i j -> Random.int 100) in
  assert (equal (mul m m') m');
  assert (equal (mul m' m) m')

(* on calcule Fib(n) (voir exercices 10.11 et 10.12) avec une matrice
   inutilement grande (1000x1000) *)

let fib n =
  let size = 1000 in
  let mfib = init size size
    (fun i j -> if i = 0 && j <= 1 || i = 1 && j = 0 then 1 else 0) in
  try M.find 1 (power mfib n).rows.(0) with Not_found -> 0

let () =
  assert (fib 0 = 0);
  assert (fib 10 = 55);
  assert (fib 14 = 377)


This document was generated using caml2html