Dynamic Programming

fibは末尾再帰にすると高速になるという説明は、どこか論点がはずれているような気がする。いや、まちがっちゃいないんだけどさ。

さて、DPといえばボトムアップに計算するのが通例のよう(なの?)だが、どちらかというとトップダウンに計算して、キャッシュさせた方が見通しがよくて好きなのだ。いわゆる、Haskellのあれか? なんか先日Schemerに聞いたら、なんかそれは違うとか何とかいってたような、なんだったっけか。
てなわけで、参照透過な任意の再帰関数をDP化させるための関数を作ってみる。

let cached n f =
  let cache = Array.make n None in
  let rec f' x =
    let i = Hashtbl.hash x mod n in
    match cache.(i) with
    | Some (w, y) when w = x -> y (* hit *)
    | _ -> begin (* miss *)
        let y = f f' x in
        cache.(i) <- Some (x, y);
        y
      end in
  f'

let fib f x =
  if x <= 1 then 1
  else f (x - 1) + f (x - 2)

let rec normal_fib x = fib normal_fib x

let dp_fib = cached 100 fib

cachedにキャッシュサイズと関数を渡すと、DP化した関数が返ってくる寸法。うまく再帰させること考えると、fibに再帰の時に使う関数を渡さなくてはならなくて、変な感じになってしまう。イマイチ。