当应用于斐波那契数列时,ocaml回忆录失败
我尝试使用记忆技术来优化斐波那契的计算。我的代码是:当应用于斐波那契数列时,ocaml回忆录失败,ocaml,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
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
差别不大。。为什么?更糟糕的是,当我试图用
running_time ggfib 40;;
running_time memoggfib 40;;
程序似乎进入无限循环并停止输出
这里怎么了?什么问题我没有解决
我修改了上面的代码,引入了一个用于记忆的“静态”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;;
在我看来,你只是在记最外面的电话。内部调用是到ggfib,而不是到(memo ggfib) 调用memoggfib时,memo函数将记住最外层调用的值。但是,内部调用由ggfib(传递给memo的函数)处理。如果你看一下ggfib的定义,你会发现它调用自己。它不呼叫(备忘录ggfib) 我看不到一种将普通(递归)函数转换为记忆函数的方法。它不会在内部自动调用自己的记忆版本
如果你从一个要记忆的函数开始,我仍然会看到“打结”的问题。对不起,我不明白。你能把它扩展一点吗?@lkahtz:问题是
ggfib
调用ggfib
,而不是memo ggfib
。这意味着您对memoggfib
的调用将执行一次检查,以查看以前是否使用该参数调用过它,然后调用ggfib
;它多次调用ggfib
。非常感谢!!!请允许我考虑一下你的帖子。对于一个新的学习者来说,有很多东西需要咀嚼。
Time lapse:0.581918
Time lapse:0.577813
(* 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 *)