Memoization

Indices and tables

Overview

  • Memoization is a programming technique used to increase the speed of program execution by storing function results so that they can be used later on.

Factorial Example

  • Consider the following function used to calculate factorials:

    1
    2
    3
    4
    5
    6
    #!/usr/bin/python3
    
    def factorial(k):
        if k < 2:
            return 1
        return k * factorial(k - 1)
    
  • Since this is a recursive function, it is easy to see how this function would get expensive for large values of ‘k’. But what if we didn’t have to recompute the results each time?

  • We can ‘memoize’ the function:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/usr/bin/python3
    
    cache = {}
    def factorial(k):
        if k < 2:
            return 1
        if k not in cache:
            cache[k] = k * factorial(k - 1)
        return cache[k]
    
  • The above example simply adds caching to the function itself. A more elegant solution, however, might be to wrap the function using a special ‘Memoize’ class

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    #!/usr/bin/python3
    
    class Memoize:
       def __init__(self, fn):
           self.fn = fn
           self.memo = {}
       def __call__(self, *args):
           if args not in self.memo:
                   self.memo[args] = self.fn(*args)
               return self.memo[args]
    
    @Memoize
    def factorial(k):
        if k < 2:
            return 1
        return k * factorial(k - 1)
    

    Note

    The example above makes use of a decorator. This is equivalent to factorial = Memoize(factorial).

  • Finally, Python actually has a built-in decorator in the functools module, ‘lru_cache’:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/usr/bin/python3
    
    import functools
    
    @functools.lru_cache
    def factorial(k):
        if k < 2:
            return 1
        return k * factorial(k - 1)
    
  • This director has an optional maxsize argument which allows the programmer to specify the maximum size of the cache

Challenge

  • Try memoizing this fibonacci sequence method in different ways. Compare them using the timeit module.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/usr/bin/python3
    
    def fibonacci(n):
        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return fibonacci(n - 1) + fibonacci(n - 2)