├── 20161005_Dynamic_Programming_Workshop_v1.key ├── fries.py ├── house_robber.py ├── string_decodings.py ├── fibonacci_examples.py └── readme.md /20161005_Dynamic_Programming_Workshop_v1.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james727/Dynamic-Programming-Workshop/HEAD/20161005_Dynamic_Programming_Workshop_v1.key -------------------------------------------------------------------------------- /fries.py: -------------------------------------------------------------------------------- 1 | def ways_to_eat( n ): 2 | w0, w1 = 1, 1, 3 | for i in range( 2, n + 1 ): 4 | w1, w0 = w1 + w0, w1 5 | return w1 6 | 7 | if __name__ == "__main__": 8 | assert ways_to_eat( 4 ) == 5 9 | -------------------------------------------------------------------------------- /house_robber.py: -------------------------------------------------------------------------------- 1 | # From https://leetcode.com/problems/house-robber/ 2 | 3 | def house_robber( H ): 4 | # Takes a list of house values and returns the max 5 | # money a robber can make robbing houses such that 6 | # no two adjacent houses are robbed 7 | R = [ H[ 0 ], max( H[ 0 ], H[ 1 ] )] # base cases 8 | for i in range( 2, len( H ) ): 9 | case1 = R[ i - 1 ] # House i isn't robbed 10 | case2 = H[ i ] + R[ i - 2 ] # House i is robbed 11 | R.append( max( case1, case2 ) ) 12 | return R[ -1 ] # Return final element of R 13 | -------------------------------------------------------------------------------- /string_decodings.py: -------------------------------------------------------------------------------- 1 | # From https://leetcode.com/problems/decode-ways/ 2 | 3 | def numDecodings( s ): 4 | # Number of possible string decodings, as described in lecture. 5 | # Doesn't handle silly corner cases for clarity (e.g., will break 6 | # on invalid input / 0 length strings) 7 | array = [ 1 ] # base case 8 | for i in range( 1, len( s ) ): 9 | 10 | # Pull off previous 2 characters to check for 1 and 2 number encodings 11 | char = s[ i ] 12 | prev_char = s[ i - 1 ] 13 | 14 | # Either our new number could be used to form a 1-letter encoding, 15 | # or a 2-letter encoding 16 | case1 = array[ i - 1 ] if char != "0" else 0 17 | case2 = array[ i - 2 ] if int( prev_char + char ) in range( 10, 27 ) else 0 18 | 19 | # Combine by summing 20 | array.append( case1 + case2 ) 21 | 22 | return array[ -1 ] 23 | -------------------------------------------------------------------------------- /fibonacci_examples.py: -------------------------------------------------------------------------------- 1 | import time 2 | import matplotlib.pyplot as plt 3 | 4 | def recursive_fibonacci( n ): 5 | # Calculates the nth Fibonacci number 6 | if n == 0: return 0 7 | elif n == 1: return 1 8 | else: 9 | return recursive_fibonacci( n - 1 ) + \ 10 | recursive_fibonacci( n - 2 ) 11 | 12 | def dynamic_fibonacci( n ): 13 | # Calculates the nth Fibonacci number iteratively 14 | # (all vals stored in a list for clarity) 15 | if n == 0: return 0 16 | elif n == 1: return 1 17 | else: 18 | vals = [ 0, 1 ] 19 | for i in range( 2, n + 1 ): 20 | vals.append( vals[ i - 1 ] + vals[ i - 2 ] ) 21 | return vals[ -1 ] 22 | 23 | memoizer = {} 24 | def memoizing_fibonacci( n ): 25 | # Calculates the nth Fibonacci number recursively 26 | # with memoization (top-down) 27 | global memoizer 28 | try: return memoizer[ n ] 29 | except KeyError: 30 | if n == 0: fib = 0 31 | elif n == 1: fib = 1 32 | else: fib = memoizing_fibonacci( n - 1 ) + \ 33 | memoizing_fibonacci( n - 2 ) 34 | memoizer[ n ] = fib 35 | return fib 36 | 37 | def test(): 38 | # Quick and dirty test for accuracy 39 | fib_nums = [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ] 40 | for index, num in enumerate( fib_nums ): 41 | assert recursive_fibonacci( index ) == num 42 | assert dynamic_fibonacci( index ) == num 43 | 44 | def time_fib(): 45 | # Timing routine. Will take 2+ minutes to run for max n = 40 46 | print "Beginning timing" 47 | for i in range(0, 40, 5): 48 | t0 = time.time() 49 | _ = recursive_fibonacci( i ) 50 | t_recursive = time.time() - t0 51 | 52 | t0 = time.time() 53 | _ = memoizing_fibonacci( i ) 54 | t_dynamic = time.time() - t0 55 | print "N = %02d, t_recursive = %04f, t_dynamic = %04f" % ( i, t_recursive, t_dynamic ) 56 | 57 | def plot_fib(): 58 | # Plot routine 59 | times_recursive = [] 60 | times_dynamic = [] 61 | 62 | for i in range( 35 ): 63 | t0 = time.time() 64 | _ = recursive_fibonacci( i ) 65 | times_recursive.append( time.time() - t0 ) 66 | 67 | t0 = time.time() 68 | _ = dynamic_fibonacci( i ) 69 | times_dynamic.append( time.time() - t0 ) 70 | 71 | plt.subplot(211) 72 | plt.plot( range( 35 ), times_dynamic, "r--", range( 35 ), times_recursive, "b--" ) 73 | plt.title("Linear Scale") 74 | plt.ylabel("Execution time (seconds)") 75 | 76 | plt.subplot(212) 77 | plt.plot( range( 35 ), times_dynamic, "r--", range( 35 ), times_recursive, "b--" ) 78 | plt.title("Logarithmic Scale") 79 | plt.xlabel("N") 80 | plt.ylabel("Execution time (seconds)") 81 | plt.yscale('log') 82 | plt.show() 83 | 84 | if __name__ == "__main__": 85 | time_fib() 86 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Dynamic programming workshop 2 | This repository contains materials for a workshop on dynamic programming given at the Recurse Center on October 6, 2016. It includes source code for all examples discussed, the presentation document, and most importantly, a list of resources / tips in this readme. 3 | 4 | ## Resources for learning and practicing 5 | In terms of learning the ideas and theory behind dynamic programming, I'd like to recommend 6 | the lectures in part 2 of the [Stanford Algorithms course on Coursera](https://www.coursera.org/learn/algorithm-design-analysis-2). 7 | You can skip right to the dynamic programming lectures, they're in week 3. A lot of the materials for 8 | this workshop were drawn from these lectures so some things will be familiar. 9 | 10 | Besides that, I'd recommend 2 things: 11 | 12 | 1. Practicing lots of problems in the dynamic programming category on [Leetcode](https://leetcode.com/tag/dynamic-programming/) 13 | 2. Searching for other videos / explanations of some of the more "classic" DP algorithms. A couple I'd recommend learning about are: 14 | * The Coin Change problem 15 | * String edit distance (helps you solve a whole class of string matching problems, like RegEx matching, etc. I found [this](https://www.youtube.com/watch?v=8Q2IEIY2pDU) video and [this](https://www.youtube.com/watch?v=eAVGRWSryGo) video to be really helpful, both from a Coursera course on gene sequencing) 16 | * The Knapsack problem (covered in the workshop) 17 | * The Longest Common Subsequence algorithm 18 | 19 | ## Curated practice problems 20 | Here I've listed some of my favorite dynamic programming problems on leetcode (and a couple from Project Euler), ordered by difficulty. 21 | 22 | #### Easier problems 23 | These are pretty similar to the ones we did in workshop (with 1 dimension) 24 | 1. [Climbing stairs](https://leetcode.com/problems/climbing-stairs/) (similar to a problem covered in workshop) 25 | 2. [House robber](https://leetcode.com/problems/house-robber/) (covered in workshop) 26 | 3. [Stock selling](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/) (a variant on classic DP principles) 27 | 4. [Max sum subarray](https://leetcode.com/problems/maximum-subarray/) (a little more difficult) 28 | 29 | #### Medium-ish problems 30 | I tried to include some 2 dimensional problems here, as well as more complex string / sequence problems. 31 | 1. [Subsequence check](https://leetcode.com/problems/is-subsequence/) (First introduction to string comparison dynamic programming) 32 | 2. [Longest wiggle subsequence](https://leetcode.com/problems/wiggle-subsequence/) (More sequence/subsequence practice) 33 | 3. [Min path sum](https://leetcode.com/problems/minimum-path-sum/) (An introduction to (usually contrived) pathfinding DP problems) 34 | 4. [More pathfinding](https://projecteuler.net/problem=82) (Slightly harder pathfinding) 35 | 5. [Coin change](https://leetcode.com/problems/coin-change/) (Another classic) 36 | 6. [Min perfect square sum](https://leetcode.com/problems/perfect-squares/) (Good 2d problem) 37 | 38 | #### Hard problems 39 | An assortment of problems of the more difficult variety. 40 | 1. [More stock buying](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/) (A tough variation to the max profit problem) 41 | 2. [Count unique BSTs](https://leetcode.com/problems/unique-binary-search-trees/) (No comment) 42 | 3. [Edit distance](https://leetcode.com/problems/edit-distance/) (Would recommend watching the videos listed above for this one. Maybe give it a shot though?) 43 | 4. [Wildcard / regex matching](https://leetcode.com/problems/wildcard-matching/) (Can be solved with dynamic programming, but I think there are more efficient non-DP solutions) 44 | 45 | For any of these problems, I would highly recommend looking at the discussion forum on Leetcode once you've finished your solution. I've learned more from people on there than most other sources combined. 46 | --------------------------------------------------------------------------------