应用于Fibonacci系列时,ocaml memoization失败

时间:2012-03-25 00:43:55

标签: ocaml

我尝试使用记忆技术来优化斐波纳契的计算。我的代码是:

let memo f = 
  let vtable = ref [] in
  let rec match_function x vt=
    match vt with
      |(x',y)::_ when x=x' -> y
      |_::l ->
        match_function x l
      |[] ->
        let y = (f x) in
        vtable :=  (x,y):: !vtable;
        y 
  in
  (fun x -> (match_function x !vtable));;

let rec ggfib = function
  0|1 as i -> i
  |x -> ggfib(x-1) + ggfib(x-2);;

let memoggfib = memo ggfib;;

let running_time f x =
  let start_time = Sys.time () in
  let y = f x in
  let finish_time = Sys.time() in
  Printf.printf "Time lapse:%f\n"  (finish_time -. start_time);
  y;;


running_time ggfib 30;;
running_time memoggfib 30;;

输出结果为:

Time lapse:0.357187
Time lapse:0.353663

差别不是那么多..为什么?更糟糕的是,当我尝试使用

计算40的斐波那契时
running_time ggfib 40;;
running_time memoggfib 40;;

程序似乎遇到无限循环并停止输出。

这里有什么问题?我没有处理什么问题?

我更改了上面的代码,为memoization引入了一个'静态'vtable。

let rec ggfib = function
  0|1 as i -> i
  |x -> ggfib(x-1) + ggfib(x-2);;

let running_time x0 =
  let vtable = ref [] in
  let start_time = Sys.time () in
  let x = ref 1 in
  let rec match_function ff x vt=
    match vt with
      |(x',y)::_ when x=x' -> y
      |_::l ->
        match_function ff x l
      |[] ->
        let y = (ff x) in
        vtable :=  (x,y):: !vtable;
        y 
  in
  let y=ref 1 in
  while !x<x0 do
    y:= match_function ggfib !x !vtable;
    x:=!x+1;
  done;
  let finish_time = Sys.time() in
  Printf.printf "Time lapse:%f\n"  (finish_time -. start_time);
  y;;


let running_time2 x0=
  let start_time = Sys.time () in
  let x = ref 1 in
  while !x<x0 do
  ggfib !x;
  x:=!x+1;
  done;
  let finish_time = Sys.time() in
  Printf.printf "Time lapse:%f\n"  (finish_time -. start_time);;

running_time 40;;
running_time2 30;;

它仍然基本相同。我没有看到显着的改善....

Time lapse:0.581918
Time lapse:0.577813

2 个答案:

答案 0 :(得分:3)

在我看来,你只是在记忆最外面的电话。内部调用是ggfib,而不是(memo ggfib)。

当您调用memoggfib时,备忘录功能将记住最外层呼叫的值。但是,内部调用由ggfib(您传递给memo的函数)处理。如果你看一下ggfib的定义,你会看到它自己调用。它没有调用(memo ggfib)。

我没有看到将普通(递归)函数转换为memoized函数的方法。它不会在内部自动调用自己的memoized版本。

如果您从一个打算记忆的功能开始,我仍然会看到“打结”的问题。

答案 1 :(得分:2)

(* a "derecursified" version of fibonacci: recursive calls are
  replaced by calls to a function passed as parameter *)
let rec fib_derec f = function
| 0|1 as i -> i
| n -> f (n - 1) + f (n - 2)

(* to get the fibonacci back we need to compute a fixpoint:
   fib_derec should get passed 'fib' as parameter,
   which we will define in term of fib_derec
*)
let rec fib n = fib_derec fib n

let test = Array.init 10 fib
(* [|0; 1; 1; 2; 3; 5; 8; 13; 21; 34|] *)

(* we can make this construction generic *)
let rec fix derec input = derec (fix derec) input

let fib = fix fib_derec

let test = Array.init 10 fib
(* [|0; 1; 1; 2; 3; 5; 8; 13; 21; 34|] *)


(* Trick: we can use this tying-the-knot operator to insert
   memoization "between the recursive calls" of the recursive function *)

let rec memo_fix table derec =
  fun input ->
    try Hashtbl.find table input with Not_found ->
      let result = derec (memo_fix table derec) input in
      Hashtbl.add table input result;
      result

let fib_table = Hashtbl.create 100 
let fib = memo_fix fib_table fib_derec

let test = Array.init 10 fib
(* [|0; 1; 1; 2; 3; 5; 8; 13; 21; 34|] *)

let test2 = fib 1000
(* -591372213: overflow, but quick result *)