2

I am working on the classic making change with coins problem with Python. This is my implementation.

def memo(fn):
    def helper(*args): # here, * indicate the fn take arbitrary number of argumetns
        d = {}
        if args in d:
            return d[args] # args is a tuple, immutable, hashable
        else:
            res = fn(*args) # here * expand a tuple as arguments
            d[args] = res
            return res
    return helper

@memo
def change(options, n):
    if n < 0 or options ==():
        return 0
    elif n == 0:
        return 1
    else:
        return change(options, n- options[0]) + change(options[1:], n)

And it turns out, the memoized version is even slower than original version! Why? What is going wrong in my implementation?

This is without memoization:

In [172]: %timeit change((50, 25, 10, 5, 1), 100)
100 loops, best of 3: 7.12 ms per loop

This is with memoization:

In [170]: %timeit change((50, 25, 10, 5, 1), 100)
10 loops, best of 3: 21.2 ms per loop
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Lucas Shen
  • 327
  • 6
  • 14
  • 3
    You're creating a new *"cache"* dictionary **every time the decorated function is called** – jonrsharpe Apr 25 '15 at 07:03
  • 4
    The term for this technique is "memoization", not "memorization". Don't believe your spell-checker. – Andrew Janke Apr 25 '15 at 07:14
  • Thanks for correction. I always think it is memorization orz. Now I know how powerful human brain could automatically fill in illusions XD – Lucas Shen Apr 25 '15 at 07:44

1 Answers1

7

In your current code:

def memo(fn):
    def helper(*args):
        d = {}

you create a new "cache" dictionary d every time the decorated function is called. It's no wonder it's slower! The minimal fix is:

def memo(fn):
    d = {}
    def helper(*args):

but it could be neater generally. I use:

def memo(func):
    def wrapper(*args):
        if args not in wrapper.cache:
            wrapper.cache[args] = func(*args)
        return wrapper.cache[args]
    wrapper.cache = {}
    return wrapper

This makes it easier to access a decorated function's cache for bug fixing etc.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437