I wrote a Python module to learn about memoization, and I have some questions on what I did.
- How pythonic is this?
- Does it make a difference if I used a class attribute instead of a function attribute for my
lookup_table
? - How can I modify
multi_run_chrono
to use it like Python's decorators (with@
)? - Should I keep the
try... except KeyError
or rather doif key in dictionary...
? - Are there some nice tricks I could do to improve this code's elegance and/or performance?
Here is the code:
from __future__ import print_function
from random import randint
from time import clock
def naive_factorial(n):
"""calculates n! with a simple recursive algorithm"""
if n == 0:
return 1
else:
return n * naive_factorial(n-1)
def memo_factorial(n):
"""calculates n! using memoization in a recursive algorithm"""
if not hasattr(memo_factorial, 'lookup_table'):
memo_factorial.lookup_table = {0: 1}
try:
return memo_factorial.lookup_table[n]
except KeyError:
val = n * memo_factorial(n - 1)
memo_factorial.lookup_table[n] = val
return val
def check_correctness():
print('Checking both functions do the same things')
for i in [0, 1, 4, 10, 22, 63]:
n = naive_factorial(i)
m = memo_factorial(i)
print('n = %d -- Naive : %d, Memo : %d' % (i, n, m))
if n != m:
print('Error in functions implementation')
exit()
def multi_run_chrono(func, nb_runs, min_val, max_val):
print('Running %s %d times, with n from %d to %d'
% (func.__name__, nb_runs, min_val, max_val))
tests = [randint(min_val, max_val) for _ in xrange(nb_runs)]
starting_time = clock()
for n in tests:
func(n)
ending_time = clock()
print('Duration : %fs' % (ending_time - starting_time))
def main():
# check_correctness() # comment/uncomment to check or not
for func in [naive_factorial, memo_factorial]:
multi_run_chrono(func, 20000, 10, 200)
if __name__ == '__main__':
main()
Benchmarking on my computer :
Running naive_factorial 20000 times, with n from 10 to 200
Duration : 0.596933s
Running memo_factorial 20000 times, with n from 10 to 200
Duration : 0.006060s
All remarks are welcome, thank you very much!
functools.lru_cache
, also consider separating the algoritmh from the memoization logic. – Caridorc Aug 5 '16 at 9:28naive_factorial
to make it use memoization instead of creating a whole new function with memoization integrated? – BusyAnt Aug 5 '16 at 9:32