lec23 - CS 3110 Lecture 23 Memoization Even when...

Info iconThis preview shows pages 1–3. Sign up to view the full content.

View Full Document Right Arrow Icon
CS 3110 Lecture 23 Memoization Even when programming in a functional style, O (1) mutable map abstractions like arrays and hash tables can be extremely useful. One important use of hash tables is for memoization , in which a previously computed result is stored in the table and retrieved later. Memoization is a powerful technique for building efficient algorithms, especially in a functional language. For example, consider the problem of computing the nth Fibonacci number, defined as f ( n ) = f ( n −1) + f ( n −2). We can translate this directly into code: let f(n) = if n<2 then 1 else f(n-1) + f(n-2) Unfortunately, this code takes exponential time: Θ(φ n ), where φ is the golden ratio, (1 + √5)/2. We can easily verify these asymptotic bounds by using the substitution method. Using the recurrence T ( n ) = T ( n −1) + T ( n −2) + 1, we show by induction that T ( n )≤φ n −1: T ( n ) = T ( n −1) + T ( n −2) + 1 k φ n −1 − 1 + k φ n −2 −1 + 1 k φ n −1 + k φ n −2 − 1 But φ has the property that φ 2 = φ + 1, so: k φ n −1 + k φ n −2 = k φ n −2 (1 + φ) = k φ n −2 φ 2 = k φ n Therefore T ( n ) ≤ k φ n − 1 and T is O n ). The Ω direction is shown similarly. The key observation is that the recursive implementation is inefficient because it recomputes the same Fibonacci numbers over and over again. If we record Fibonacci numbers as they are computed, we can avoid this redundant work. The idea is that whenever we compute f(n) , we store it in a table indexed by n . In this case the indexing keys are integers, so we can use implement this table using an array: let fibm(n) = let memo: int option array = Array.create (n+1) None in let rec f_mem(n) = match memo.(n) with Some result -> result (* computed already! *) | None -> let result = if n<2 then 1 else f_mem(n-1) + f_mem(n-2) in memo.(n) <- (Some result); (* record in table *) result in f_mem(n) The function f_mem defined inside fibm contains the original recursive algorithm, except before doing that calculation it first checks if the result has already been computed and stored in the table in which case it simply returns the result. How do we analyze the running time of this function? The time spent in a single call to f_mem is O(1) if we exclude the time spent in any recursive calls that it happens to make. Now we
Background image of page 1

Info iconThis preview has intentionally blurred sections. Sign up to view the full version.

View Full DocumentRight Arrow Icon
look for a way to bound the total number of recursive calls by finding some measure of the progress that is being made. A good choice of progress measure, not only here but also for many uses of memoization, is the number of nonempty entries in the table (i.e. entries that contain Some integer value rather than None ). Each time f_mem makes the two recursive calls it also increases the number of nonempty entries by one (filling in a formerly empty entry in the table with a new value). Since the table has only n entries, there can thus only be a total of O(n) calls to f_mem , for a total running time of O(n) (because we established above that each call
Background image of page 2
Image of page 3
This is the end of the preview. Sign up to access the rest of the document.

Page1 / 6

lec23 - CS 3110 Lecture 23 Memoization Even when...

This preview shows document pages 1 - 3. Sign up to view the full document.

View Full Document Right Arrow Icon
Ask a homework question - tutors are online