Slide 1

Slide 1 text

Dynamic Programming or, why Aaron Patterson should give me $50 So I’m going to talk about Dynamic Programming, which is something I learned while trying to do the September coding challenge. I’m pretty sure I didn’t win, but in trying to solve the problems on Project Euler I ended up yak-shaving myself into giving this talk. So.

Slide 2

Slide 2 text

Your Mission: Project Euler So, I had a long a complicated history with Project Euler. I first encountered maybe 4 years ago, and hated it, probably bc at the time, while I really liked math, I didn’t actually know how to program, and due to reasons I first attempted them in Java. Fast forward to last year, and I came back to PE as a way to help me learn Ruby, and it was a completely different. I loved it!* Because math! And coding is fun! It impressed all my friends! I did about a dozen or so, and then a bunch more during the May Seattle.rb coding challenge.

Slide 3

Slide 3 text

Your Mission: Project Euler • Math! So, I had a long a complicated history with Project Euler. I first encountered maybe 4 years ago, and hated it, probably bc at the time, while I really liked math, I didn’t actually know how to program, and due to reasons I first attempted them in Java. Fast forward to last year, and I came back to PE as a way to help me learn Ruby, and it was a completely different. I loved it!* Because math! And coding is fun! It impressed all my friends! I did about a dozen or so, and then a bunch more during the May Seattle.rb coding challenge.

Slide 4

Slide 4 text

Your Mission: Project Euler • Math! • Coding! So, I had a long a complicated history with Project Euler. I first encountered maybe 4 years ago, and hated it, probably bc at the time, while I really liked math, I didn’t actually know how to program, and due to reasons I first attempted them in Java. Fast forward to last year, and I came back to PE as a way to help me learn Ruby, and it was a completely different. I loved it!* Because math! And coding is fun! It impressed all my friends! I did about a dozen or so, and then a bunch more during the May Seattle.rb coding challenge.

Slide 5

Slide 5 text

Your Mission: Project Euler • Math! • Coding! • Appearing badass! So, I had a long a complicated history with Project Euler. I first encountered maybe 4 years ago, and hated it, probably bc at the time, while I really liked math, I didn’t actually know how to program, and due to reasons I first attempted them in Java. Fast forward to last year, and I came back to PE as a way to help me learn Ruby, and it was a completely different. I loved it!* Because math! And coding is fun! It impressed all my friends! I did about a dozen or so, and then a bunch more during the May Seattle.rb coding challenge.

Slide 6

Slide 6 text

Your Mission: Project Euler • Math! • Coding! • Appearing badass! So, I had a long a complicated history with Project Euler. I first encountered maybe 4 years ago, and hated it, probably bc at the time, while I really liked math, I didn’t actually know how to program, and due to reasons I first attempted them in Java. Fast forward to last year, and I came back to PE as a way to help me learn Ruby, and it was a completely different. I loved it!* Because math! And coding is fun! It impressed all my friends! I did about a dozen or so, and then a bunch more during the May Seattle.rb coding challenge.

Slide 7

Slide 7 text

Your Mission: Project Euler • Math! • Coding! • Appearing badass! • So, I had a long a complicated history with Project Euler. I first encountered maybe 4 years ago, and hated it, probably bc at the time, while I really liked math, I didn’t actually know how to program, and due to reasons I first attempted them in Java. Fast forward to last year, and I came back to PE as a way to help me learn Ruby, and it was a completely different. I loved it!* Because math! And coding is fun! It impressed all my friends! I did about a dozen or so, and then a bunch more during the May Seattle.rb coding challenge.

Slide 8

Slide 8 text

But I was quickly hitting a wall. PE problems get really hard really fast, and I found myself getting pretty stuck on a certain few.

Slide 9

Slide 9 text

Problem 18: Maximum Sum Path So this was one of the first problems that I ended up getting stuck on for a very long time. It wasn’t obvious to me how to efficiently traverse the pyramid to find the optimal sum—every solution I came up with was either brute forcey, or wrong, or both.

Slide 10

Slide 10 text

Problem 31 How many ways can you make change out of 2£ using 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p)? This was another one I got stuck on for the longest time. It’s a fairly classic problem, and from hearing a talk given at Seattle.rb, I knew there was a recursive way of solving it, but recursion was breaking my head and I just couldn’t do it.

Slide 11

Slide 11 text

Not feeling so badass.

Slide 12

Slide 12 text

:( Not feeling so badass.

Slide 13

Slide 13 text

But upon closer inspection…. But after poking around the internet for help, it turned out that all these problems I was getting super stuck on had something in common.

Slide 14

Slide 14 text

DYNAMIC PROGRAMMING! a.k.a. DP And that’s how I learned about Dynamic Programming!

Slide 15

Slide 15 text

So, what exactly is DP? • A method for solving complex problems by breaking them down into simpler subproblems. • Useful for problems that compute the same subproblems over and over.
 So the thing I was finding in common was that all the problems could be broken up in to smaller sub-problems and these smaller subproblems are in turn divided in to still-smaller ones. It quickly became clear that there were some over-lappping subproblems, (big hint for DP) and because I was ending up calculating all those subproblems each time over and over, my program was slooowwww.

Slide 16

Slide 16 text

Two types of DP Top-Down: Store previously computed results, a.k.a. use Memoization.
 Bottom-Up: Get the optimal solution for a subproblem starting from the end, and working back up.
 Top-Down : Start solving the given problem by breaking it down. If you see that the problem has been solved already, then just return the saved answer. If it has not been solved, solve it and save the answer. This is usually easy to think of and very intuitive. This is referred to as Memoization. Bottom-Up : Analyze the problem and see the order in which the sub-problems are solved and start solving from the trivial subproblem, up towards the given problem. In this process, it is guaranteed that the subproblems are solved before solving the problem. This is referred to as Dynamic Programming.


Slide 17

Slide 17 text

Fibonacci Sequence But if you’re like me, I learn better by example. So let’s take a familiar problem, such as implementing a way of finding the nth Fibonacci number. (We’re going to use the top-down approach here.) Fibonacci sequence is a sequence of numbers* where each number is the sum of the two previous fibonacci numbers. Here’s a basic iterative implementation of it, which works, but is kind of boring.

Slide 18

Slide 18 text

Fibonacci Sequence 1, 1, 2, 3, 5, 8, 13, 21, 34, 55… But if you’re like me, I learn better by example. So let’s take a familiar problem, such as implementing a way of finding the nth Fibonacci number. (We’re going to use the top-down approach here.) Fibonacci sequence is a sequence of numbers* where each number is the sum of the two previous fibonacci numbers. Here’s a basic iterative implementation of it, which works, but is kind of boring.

Slide 19

Slide 19 text

Fibonacci Sequence 1, 1, 2, 3, 5, 8, 13, 21, 34, 55… But if you’re like me, I learn better by example. So let’s take a familiar problem, such as implementing a way of finding the nth Fibonacci number. (We’re going to use the top-down approach here.) Fibonacci sequence is a sequence of numbers* where each number is the sum of the two previous fibonacci numbers. Here’s a basic iterative implementation of it, which works, but is kind of boring.

Slide 20

Slide 20 text

Fibonacci Sequence Recursive implementation This kind of problem is fortunately great for getting beginners to think about recursion. So here’s another implementation of the same problem of finding the nth fibonacci number, except using recursion. When I first understood how recursion worked, I was like…

Slide 21

Slide 21 text

SO AMAZE So amaze! Recursion is so elegant! Only two lines of code! Wow!

Slide 22

Slide 22 text

SO AMAZE So amaze! Recursion is so elegant! Only two lines of code! Wow!

Slide 23

Slide 23 text

require ‘benchmark’ Benchmark.bm do |x|
 x.report { recursive_fib 10 }
 x.report { recursive_fib 20 }
 x.report { recursive_fib 30 }
 x.report { recursive_fib 40 } x.report { recursive_fib 45 }
 end But wait a minute. Turns out recursion isn’t all that great in this scenario. Using benchmark to time how long it took to compute the 10th, 20th 30th and so on fib number,(click) it takes about17 microseconds for 10, less than a millisecond for 20. But then at 30 you get more than a 10th of a second, and at 40 it takes 14 seconds, and even 45 takes over 2.5 minutes.

Slide 24

Slide 24 text

require ‘benchmark’ Benchmark.bm do |x|
 x.report { recursive_fib 10 }
 x.report { recursive_fib 20 }
 x.report { recursive_fib 30 }
 x.report { recursive_fib 40 } x.report { recursive_fib 45 }
 end #=> 0.000017 But wait a minute. Turns out recursion isn’t all that great in this scenario. Using benchmark to time how long it took to compute the 10th, 20th 30th and so on fib number,(click) it takes about17 microseconds for 10, less than a millisecond for 20. But then at 30 you get more than a 10th of a second, and at 40 it takes 14 seconds, and even 45 takes over 2.5 minutes.

Slide 25

Slide 25 text

require ‘benchmark’ Benchmark.bm do |x|
 x.report { recursive_fib 10 }
 x.report { recursive_fib 20 }
 x.report { recursive_fib 30 }
 x.report { recursive_fib 40 } x.report { recursive_fib 45 }
 end #=> 0.000017 #=> 0.000947 But wait a minute. Turns out recursion isn’t all that great in this scenario. Using benchmark to time how long it took to compute the 10th, 20th 30th and so on fib number,(click) it takes about17 microseconds for 10, less than a millisecond for 20. But then at 30 you get more than a 10th of a second, and at 40 it takes 14 seconds, and even 45 takes over 2.5 minutes.

Slide 26

Slide 26 text

require ‘benchmark’ Benchmark.bm do |x|
 x.report { recursive_fib 10 }
 x.report { recursive_fib 20 }
 x.report { recursive_fib 30 }
 x.report { recursive_fib 40 } x.report { recursive_fib 45 }
 end #=> 0.000017 #=> 0.000947 #=> 0.114328 But wait a minute. Turns out recursion isn’t all that great in this scenario. Using benchmark to time how long it took to compute the 10th, 20th 30th and so on fib number,(click) it takes about17 microseconds for 10, less than a millisecond for 20. But then at 30 you get more than a 10th of a second, and at 40 it takes 14 seconds, and even 45 takes over 2.5 minutes.

Slide 27

Slide 27 text

require ‘benchmark’ Benchmark.bm do |x|
 x.report { recursive_fib 10 }
 x.report { recursive_fib 20 }
 x.report { recursive_fib 30 }
 x.report { recursive_fib 40 } x.report { recursive_fib 45 }
 end #=> 0.000017 #=> 0.000947 #=> 0.114328 #=> 14.128237 But wait a minute. Turns out recursion isn’t all that great in this scenario. Using benchmark to time how long it took to compute the 10th, 20th 30th and so on fib number,(click) it takes about17 microseconds for 10, less than a millisecond for 20. But then at 30 you get more than a 10th of a second, and at 40 it takes 14 seconds, and even 45 takes over 2.5 minutes.

Slide 28

Slide 28 text

require ‘benchmark’ Benchmark.bm do |x|
 x.report { recursive_fib 10 }
 x.report { recursive_fib 20 }
 x.report { recursive_fib 30 }
 x.report { recursive_fib 40 } x.report { recursive_fib 45 }
 end #=> 0.000017 #=> 0.000947 #=> 0.114328 #=> 14.128237 #=> 159.97751 But wait a minute. Turns out recursion isn’t all that great in this scenario. Using benchmark to time how long it took to compute the 10th, 20th 30th and so on fib number,(click) it takes about17 microseconds for 10, less than a millisecond for 20. But then at 30 you get more than a 10th of a second, and at 40 it takes 14 seconds, and even 45 takes over 2.5 minutes.

Slide 29

Slide 29 text

Taking a look at even how the 5th Fibonacci number is computed using this recursive method, you can see that calling fib on 5 calls fib of 4 and 3, which in turn calls the function on 3 and 2 and so on. You can see that there’s a lot of repeated calls.

Slide 30

Slide 30 text

O(n!) In terms of big-O notation for those who are familiar or care about such things, that is a time complexity of O(n!). For those who aren’t familiar, that basically translates* to “Oh, no.”

Slide 31

Slide 31 text

O(n!) == O(%#@‼) In terms of big-O notation for those who are familiar or care about such things, that is a time complexity of O(n!). For those who aren’t familiar, that basically translates* to “Oh, no.”

Slide 32

Slide 32 text

But fortunately there is a way out of this!

Slide 33

Slide 33 text

DP to the rescue! But fortunately there is a way out of this!

Slide 34

Slide 34 text

Fibonacci: DP solution So as we saw in the recursive implementation, there were many repeated calls, why not store those computations? In this solution, there is an array, fibs, initialized at the start of the function. Then, finding the nth fibonacci number is just a matter of looking at the nth index in that array. I.e. this is the top down approach, where each computation is memoized in the fibs array.

Slide 35

Slide 35 text

0 1 2 3 4 5 6 7 8 0 1 1 2 3 5 8 13 21 So basically, if the nth value already exists in the array, you just look it up, and worst case scenario, it only takes you n calls to find that number. In other words, you can get the nth fibonacci number in* linear time.

Slide 36

Slide 36 text

O(n) == 0 1 2 3 4 5 6 7 8 0 1 1 2 3 5 8 13 21 So basically, if the nth value already exists in the array, you just look it up, and worst case scenario, it only takes you n calls to find that number. In other words, you can get the nth fibonacci number in* linear time.

Slide 37

Slide 37 text

The key difference here is that while n-2 and n-1 are still being used as arguments, in one it is used as an index to access a previously computed and stored result, whereas in recursion you have to actually recompute everything all the way down.

Slide 38

Slide 38 text

Maximum Sum Given an array of numbers, how would you compute which subset of numbers has the maximum sum if no two elements in the subset can be adjacent in the original array? Ex: [6, 4, 5, 7, 1, 12, 3, 2] => 27 So let’s move on to a more interesting example. How would you find the maximum sum returned by a subset of that array, if none of the elements in the subset can be adjacent? So for example, in the case of the array [6, 4, 5, 7, 1, 12, 3, 2], the possible subsets would be [6, 5, 1], [6, 7, 12], [4, 7, 12], [4, 7, 3] , [4, 1, 3] and so on. The sub-array whose elements reduces to the maximum possible sum is [6,7,12, 2], with a sum of 27. So, does anyone have any ideas as to how you’d approach this problem?

Slide 39

Slide 39 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 [draw binary tree here] Even if you constructed all subsets of the array efficiently, you end up doing a depth first search of every possible legal combination of elements. (Explain terminal cases, how value for each index gets passed back up to the parent node)

Slide 40

Slide 40 text

Here is an example of an implementation that solves this using brute force. As we saw in the diagram of what is happening when the Fibonacci sequence function is implemented recursively, this also requires a depth-first traversal of the tree from the previous slide.

Slide 41

Slide 41 text

O(n!) Which, as we know, is not very efficient.

Slide 42

Slide 42 text

O(n!) == O(%#@‼) Which, as we know, is not very efficient.

Slide 43

Slide 43 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 44

Slide 44 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 45

Slide 45 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 46

Slide 46 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 47

Slide 47 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 48

Slide 48 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 49

Slide 49 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 50

Slide 50 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 51

Slide 51 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 52

Slide 52 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 53

Slide 53 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 54

Slide 54 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 55

Slide 55 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 56

Slide 56 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 57

Slide 57 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 + So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 58

Slide 58 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 59

Slide 59 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 60

Slide 60 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 61

Slide 61 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 62

Slide 62 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 63

Slide 63 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 64

Slide 64 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 25 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 65

Slide 65 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 25 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 66

Slide 66 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 25 25 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 67

Slide 67 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 25 25 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 68

Slide 68 text

index 0 1 2 3 4 5 6 7 value 6 4 5 7 1 12 3 2 index 0 1 2 3 4 5 6 7 max sum 6 6 4 4 6 5 11 6 13 13 25 25 27 So, in the bottom-up dynamic programming solution, you again have an array that stores the maximum sums up to that point in your iteration. You assume that anything that came before is the optimal, in this case maximum, solution. So, starting with the first element, you assume that it is included in the subset that contains the maximum sum. Since that subset at the first iteration only contains a 6, 6 is the maximum sum. Now we move to the second element. Since a subset that contains this element cannot also contain the first element (or else there would be adjacent elements in the subset), we compare which of 4 and 6 is larger and we get 6*. Moving to the next element, we know that the maximum sum at this point in the iteration has to either contain this element plus any subset that might include it. That is*, compare the sum of 5 and the previous max sum (6) and see if it’s bigger than the max sum of any subset that does not contain 5, which would be represented in the max_sum array at the previous index*. (etc)

Slide 69

Slide 69 text

Here is an implementation of this approach in Ruby.

Slide 70

Slide 70 text

require ‘benchmark’ test1 = (1..30).to_a.shuffle test2 = (1..60).to_a.shuffle Benchmark.bm do |x| x.report { brute_force test1 } #=> 0.004670
 x.report { brute_force test2 } #=> 17.847418 x.report { max_subset test1 } #=> 0.000016 x.report { max_subset test2 } #=> 0.000013 end To prove that this DP approach is in fact more efficient, we can time it. For test1, we have DP clocking in at ~4 milliseconds versus ~16 microseconds for the brute force method, e.g. 287.5 times faster, and for test2, we get ~17 seconds versus ~13 microseconds, e.g. over 1 million times faster.

Slide 71

Slide 71 text

Back to our regular scheduled programming… Problem 31: How many ways can you make change out of 2£ using 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p)? Now let’s look at the Project Euler problem mentioned at the beginning of this talk.

Slide 72

Slide 72 text

Here is a recursive implementation. I won’t go into too much detail, but if you’re interested in how it works, please watch Aja Hammerly’s excellent talk, “A World Without Assignment.” (http://confreaks.tv/videos/mwrc2014-a-world-without-assignment)

Slide 73

Slide 73 text

Storing previously calculated results 0 1 2 3 4 5 6 7 8 9 10 0 1 5 10 coins amount in cents So again, rather than re-calculating results we will need for future calculations, we store them all. Here, we use a two-dimensional chart to store these results rather than a single chart (as with the maximum sum of non-adjacent elements problem), which I will explain in a minute.

Slide 74

Slide 74 text

So let’s first consider the terminal cases of this problem. These are the two terminal cases of the coin change problem. When you have no coins, or your total amount you’re trying to make change for is negative, then there are no ways to make change. If your amount is exactly 0 and you have some amount of coins, there is exactly one way to make change.

Slide 75

Slide 75 text

Storing previously calculated results 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 5 1 10 1 amount in cents coins So we fill in the first row and column of our chart accordingly.

Slide 76

Slide 76 text

Now let’s consider the meat of the logic. We know that with dynamic programming, we want to build upon previously calculated results. In this case, the two kinds of previously calculated results will be how many ways we can make change for the given amount with all the coins except the coin with the largest value (`ways_without_max`), and how many ways we can make change for that amount minus the value of your largest denomination using all of the coins (`ways_with_max`).

Slide 77

Slide 77 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 78

Slide 78 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 79

Slide 79 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 80

Slide 80 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 81

Slide 81 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 82

Slide 82 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 83

Slide 83 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 1 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 84

Slide 84 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 1 1 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 85

Slide 85 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +1 =1 0+ 1 = 1 5 1 10 1 1 1 1 1 1 1 1 1 coins amount in cents So for example, suppose we only have pennies. Then if our amount is 1, we know from our algorithm that the number of ways to make change is to add how many ways you can make change with everything except pennies (in this case, with no coins, which we know is zero from the top row of the same column, indicated by the blue 0), and how many ways we can make change with the amount minus the value of a penny using only pennies i.e. how can we make change for $0.00 with pennies? Again, from the previously stored result in the first column second row, we know that is 1 (the red 1). And so forth.

Slide 86

Slide 86 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1+ 0 =1 1 1 1 1+1 = 2 1 +1 = 2 10 1 coins amount in cents In other words, each cell in the chart is the sum of the value inherited from row above, plus the value at amount minus max for that current row, which will always have been previously calculated.

Slide 87

Slide 87 text

count_change(amount, coins[0…-1]) + count_change(amount - max, coins) 0 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 2 2 2 2 2 3 10 1 1 1 1 1 2 2 2 2 2 4 coins amount in cents Filling in the chart, we get these values.

Slide 88

Slide 88 text

So putting together this talk ended up being kind of a yak shave. Turns out talks are a lot of work, and I actually didn’t get to many PE problems, which meant I didn’t win the $50 Aaron Patterson promised to the winner of this month’s Seattle.rb coding challenge. However, I did learn a ton, and since the coding challenge was just Seattle.rb’s way of getting more people to actively participate, I feel like giving this talk should totally count, don’t you?

Slide 89

Slide 89 text

So putting together this talk ended up being kind of a yak shave. Turns out talks are a lot of work, and I actually didn’t get to many PE problems, which meant I didn’t win the $50 Aaron Patterson promised to the winner of this month’s Seattle.rb coding challenge. However, I did learn a ton, and since the coding challenge was just Seattle.rb’s way of getting more people to actively participate, I feel like giving this talk should totally count, don’t you?

Slide 90

Slide 90 text

So what’s DP good for? • Solving problems quickly that might otherwise take a lot of time using more traditional approaches such as recursion • Examples: Coin Change, Knapsack problem, substring/DNA matching, minimum/maximum path

Slide 91

Slide 91 text

No really, what’s DP good for? • Passing technical interviews at companies who arguably don’t know how to interview properly • I don’t know

Slide 92

Slide 92 text

• http://www.codechef.com/wiki/tutorial-dynamic- programming • h t t p : / / w w w . a l g o r i t h m i s t . c o m / i n d e x . p h p / Dynamic_Programming • http://www.geeksforgeeks.org/dynamic-programming- set-10-0-1-knapsack-problem/ • http://functionspace.org/articles/32/Fibonacci-series-and- Dynamic-programming • http://confreaks.tv/videos/mwrc2014-a-world-without- assignment (Walk-through of coin-change problem implemented recursively)

Slide 93

Slide 93 text

Questions? Comments? Can we be friends? 
 …Should @tenderlove give me $50? Hsing-Hui Hsu @SoManyHs Project Euler friend key: 270590_f23cf789be3f1c9c72b26 6ffe4c2541f