Patrick Lid Monslaup
Technical manager & developer for Knowit Experience Bergen. Looking for a new job? Check out http://knowit.no/karriere
Dynamic Programming is a method for
solving a problem by
The naive approach
def fib(n):
if (n <= 0):
return 0
if (n == 1):
return 1
return fib(n-1) + fib(n-2)def fib_with_count(n):
count = [0]*(n+1)
def fib(n):
count[n] = count[n] + 1
if (n <= 0):
return 0
if (n == 1):
return 1
return fib(n-1) + fib(n-2)
res = fib(n)
print("Each fib(n) was called ",
count, " times")
print("Total fib calls was ",
sum(count))
return resWith memoization
memo = { 0: 0, 1: 1 }
def fib_memo(n):
if (n in memo):
return memo[n]
res = fib_memo(n-1) + fib_memo(n-2)
memo[n] = res
return resdef fib(n):
if (n <= 0):
return 0
if (n == 1):
return 1
return fib(n-1) + fib(n-2)Normal
Using memoization
..fib(n) only needs to know fib(n-1) and fib(n-2)
Dynamic Programming is a method for solving a problem by breaking it down into simpler subproblems,
solving each of those subproblems just once,
and storing their solutions using a memory-based data structure (array, map,etc).
Recursive fibonacci with memoization is dynamic programming, as it breaks down the problem into subproblems and solves each of them only once.
Based on an excellent article at
https://hackernoon.com/google-interview-questions-deconstructed-the-knights-dialer-f780d516f029
How many disctinct numbers can be typed, starting at number 'i' after 'n' jumps?
NEIGHBORS_MAP = {
1: (6, 8),
2: (7, 9),
3: (4, 8),
4: (3, 9, 0),
5: tuple(), # 5 has no neighbors
6: (1, 7, 0),
7: (2, 6),
8: (1, 3),
9: (2, 4),
0: (4, 6),
}1
3
6
15
Naive recursion would be exponential time
Dynamic Programming is a method for
solving a problem by
solving each subproblem just once,
neighors = { ... } # map of neighbors
def count_sequences(start_position, num_hops):
if num_hops == 0:
return 1
num_sequences = 0
for position in neighbors(start_position):
num_sequences += count_sequences(position, num_hops - 1)
return num_sequences
if __name__ == '__main__':
print(count_sequences(6, 4))
def count_sequences(start_position, num_hops):
prior_case = [1] * 10
current_case = [0] * 10
current_num_hops = 1
while current_num_hops <= num_hops:
current_case = [0] * 10
current_num_hops += 1
for position in range(0, 10):
for neighbor in neighbors(position):
current_case[position] += prior_case[neighbor]
prior_case = current_case
return current_case[start_position] Still linear memory usage
Yes, but its very hard
seq_data = get_data_sequentially(..)
threaded_data = get_data_threaded(..)
# Data must have same length
self.assertTrue(len(seq_data) == len(threaded_data))
# Each data point must match,
# but data is in random order,
# and sorting it is too slow.
# TODO Can we compare the data in linear time?
A project I'm working on has to fetch a lot of data.
Fetching the data took 1127 seconds,
which is about 19 minutes.
To speed it up we added threads to do a lot of it in paralell - how do you quickly test if this is correct?
seen_data = {} # memoization: O(1) insert and lookup
# Memo the seq data
for data in seq_data:
# False until seen by both lists
seen_data[data.id] = False
# Verify threaded data was also found seq.
for data in threaded_data:
if not data.id in seen_data:
assertTrue(False,
"Data not found {}".format(data.id))
else:
seen_data[data.id] = True
# Verify threading missed no data
for (k,v) in seen_data:
assertTrue(v,
"{} not found".format(k))By Patrick Lid Monslaup
What is dynamic programming, and how can it be leveraged to increase code quality and speed?
Technical manager & developer for Knowit Experience Bergen. Looking for a new job? Check out http://knowit.no/karriere