Memoization in OCaml?

后端 未结 2 866
南方客
南方客 2021-01-01 00:15

It is possible to improve \"raw\" Fibonacci recursive procedure

Fib[n_] := If[n < 2, n, Fib[n - 1] + Fib[n - 2]]

with

         


        
相关标签:
2条回答
  • 2021-01-01 00:50

    The solution provided by rgrinberg can be generalized so that we can memoize any function. I am going to use associative lists instead of hashtables. But it does not really matter, you can easily convert all my examples to use hashtables.

    First, here is a function memo which takes another function and returns its memoized version. It is what nlucaroni suggested in one of the comments:

    let memo f =
      let m = ref [] in
        fun x ->
          try
            List.assoc x !m
          with
          Not_found ->
            let y = f x in
              m := (x, y) :: !m ;
              y
    

    The function memo f keeps a list m of results computed so far. When asked to compute f x it first checks m to see if f x has been computed already. If yes, it returns the result, otherwise it actually computes f x, stores the result in m, and returns it.

    There is a problem with the above memo in case f is recursive. Once memo calls f to compute f x, any recursive calls made by f will not be intercepted by memo. To solve this problem we need to do two things:

    1. In the definition of such a recursive f we need to substitute recursive calls with calls to a function "to be provided later" (this will be the memoized version of f).

    2. In memo f we need to provide f with the promised "function which you should call when you want to make a recursive call".

    This leads to the following solution:

    let memo_rec f =
      let m = ref [] in
      let rec g x =
        try
          List.assoc x !m
        with
        Not_found ->
          let y = f g x in
            m := (x, y) :: !m ;
            y
      in
        g
    

    To demonstrate how this works, let us memoize the naive Fibonacci function. We need to write it so that it accepts an extra argument, which I will call self. This argument is what the function should use instead of recursively calling itself:

    let fib self = function
        0 -> 1
      | 1 -> 1
      | n -> self (n - 1) + self (n - 2)
    

    Now to get the memoized fib, we compute

    let fib_memoized = memo_rec fib
    

    You are welcome to try it out to see that fib_memoized 50 returns instantly. (This is not so for memo f where f is the usual naive recursive definition.)

    0 讨论(0)
  • 2021-01-01 01:09

    You pretty much do what the mathematica version does but manually:

    let rec fib = 
      let cache = Hashtbl.create 10 in
      begin fun n ->
        try Hashtbl.find cache n
        with Not_found -> begin
          if n < 2 then n
          else 
            let f = fib (n-1) + fib (n-2) in
            Hashtbl.add cache n f; f
        end
      end
    

    Here I choose a hashtable to store already computed results instead of recomputing them. Note that you should still beware of integer overflow since we are using a normal and not a big int.

    0 讨论(0)
提交回复
热议问题