1 00:00:00,530 --> 00:00:02,960 The following content is provided under a Creative 2 00:00:02,960 --> 00:00:04,370 Commons license. 3 00:00:04,370 --> 00:00:07,410 Your support will help MIT OpenCourseWare continue to 4 00:00:07,410 --> 00:00:11,060 offer high quality educational resources for free. 5 00:00:11,060 --> 00:00:13,960 To make a donation or view additional materials from 6 00:00:13,960 --> 00:00:19,790 hundreds of MIT courses, visit MIT OpenCourseWare at 7 00:00:19,790 --> 00:00:21,040 ocw.mit.edu. 8 00:00:24,368 --> 00:00:28,100 PROFESSOR: So this week the last final 9 00:00:28,100 --> 00:00:29,720 topic we were taught-- 10 00:00:29,720 --> 00:00:32,460 we're really going to talk about in class is dynamic 11 00:00:32,460 --> 00:00:38,065 programming, so who can tell me what the key idea in 12 00:00:38,065 --> 00:00:39,315 dynamic programming is? 13 00:00:42,092 --> 00:00:43,996 AUDIENCE: A way of [INAUDIBLE]. 14 00:00:46,860 --> 00:00:49,540 Instead of optimization tools, you can use [UNINTELLIGIBLE] 15 00:00:54,400 --> 00:00:57,270 PROFESSOR: Sort of. 16 00:00:57,270 --> 00:00:59,790 The key idea is that you don't want to repeat computations 17 00:00:59,790 --> 00:01:01,880 that you've already done before. 18 00:01:01,880 --> 00:01:06,690 So you want to find a way to only do everything once. 19 00:01:06,690 --> 00:01:10,190 So it's a form of laziness. 20 00:01:10,190 --> 00:01:13,820 There are two key attributes for a problem though that need 21 00:01:13,820 --> 00:01:18,600 to exist for it to be solvable with dynamic programming. 22 00:01:18,600 --> 00:01:20,540 Can someone tell me what those are? 23 00:01:24,977 --> 00:01:28,921 AUDIENCE: It's optimal [UNINTELLIGIBLE] 24 00:01:28,921 --> 00:01:32,372 The local optimization has those. 25 00:01:32,372 --> 00:01:40,768 PROFESSOR: There's overlapping sub-properties. 26 00:01:40,768 --> 00:01:42,211 [LAUGHTER] 27 00:01:42,211 --> 00:01:46,300 You had all the right words, just not in the right order. 28 00:01:46,300 --> 00:01:47,825 An optimal substructure. 29 00:01:56,510 --> 00:01:56,880 OK. 30 00:01:56,880 --> 00:02:00,352 Can you describe the first one, or any one? 31 00:02:00,352 --> 00:02:03,733 AUDIENCE: You get to the end solution [UNINTELLIGIBLE] 32 00:02:06,631 --> 00:02:09,370 PROFESSOR: So when they say overlapping sub-problems, it 33 00:02:09,370 --> 00:02:12,490 means that we can take the big problem and we can break it 34 00:02:12,490 --> 00:02:17,550 down to a slightly smaller instance of the same problem. 35 00:02:17,550 --> 00:02:20,520 And then we can use the solution to that smaller 36 00:02:20,520 --> 00:02:25,290 sub-problem in the solution to our bigger problem. 37 00:02:25,290 --> 00:02:28,020 And then optimal substructure is closely related. 38 00:02:28,020 --> 00:02:31,270 It's saying that if we get an optimal solution for one of 39 00:02:31,270 --> 00:02:35,560 those sub-problems, then we can use that optimal solution 40 00:02:35,560 --> 00:02:40,080 for the optimal solution of the big problem. 41 00:02:40,080 --> 00:02:42,226 Does that make sense to everyone? 42 00:02:42,226 --> 00:02:44,058 AUDIENCE: [INAUDIBLE] 43 00:02:44,058 --> 00:02:46,820 PROFESSOR: What's that? 44 00:02:46,820 --> 00:02:48,260 It's closely related. 45 00:02:48,260 --> 00:02:49,510 And so it's-- 46 00:02:53,540 --> 00:02:56,860 yeah, it's closely related but not exactly the same. 47 00:03:00,010 --> 00:03:04,240 So to start off, we're going to do kind of a function that 48 00:03:04,240 --> 00:03:06,000 we're all familiar with, is Fibonacci, right. 49 00:03:09,610 --> 00:03:12,390 We've seen this one a billion times before. 50 00:03:18,500 --> 00:03:21,860 And so it's pretty obvious where the overlapping 51 00:03:21,860 --> 00:03:23,290 sub-problems are. 52 00:03:23,290 --> 00:03:27,903 The solution to f of N is f of N minus 1, and f 53 00:03:27,903 --> 00:03:29,980 of N minus 2, right? 54 00:03:29,980 --> 00:03:33,130 And so if I combine the solutions to these two 55 00:03:33,130 --> 00:03:36,940 instances of Fibonacci, I get the solution for the big 56 00:03:36,940 --> 00:03:39,400 instance of Fibonacci right? 57 00:03:39,400 --> 00:03:47,810 So on the screen, you have an example function. 58 00:03:47,810 --> 00:03:50,640 You've all seen this before. 59 00:03:50,640 --> 00:03:53,640 And so what I'm just going to do is I'm going to run this 60 00:03:53,640 --> 00:03:59,800 Fibonacci function from 0 to 30, or N equals 29 actually. 61 00:03:59,800 --> 00:04:02,850 And we're going to look at how many 62 00:04:02,850 --> 00:04:04,100 steps it takes to execute. 63 00:04:11,730 --> 00:04:15,800 When we get to Fibonacci of 29, it takes about 1.6 million 64 00:04:15,800 --> 00:04:19,300 steps to compute the value. 65 00:04:19,300 --> 00:04:25,380 And if we take a look at a plot here-- 66 00:04:25,380 --> 00:04:31,610 so the y-axis is semi-log, the blue dots represent the actual 67 00:04:31,610 --> 00:04:34,030 number of steps that the function took, so the number 68 00:04:34,030 --> 00:04:36,690 of times that it had to perform the computation. 69 00:04:36,690 --> 00:04:41,600 And then the blue solid line represents 2 to the N. The red 70 00:04:41,600 --> 00:04:44,475 line is the golden ratio to the N, which is the tight 71 00:04:44,475 --> 00:04:48,440 bound for this version of Fibonacci. 72 00:04:48,440 --> 00:04:53,740 And then the green line is a plot of a quadratic function. 73 00:04:53,740 --> 00:04:57,130 So nothing really surprising there. 74 00:04:57,130 --> 00:05:01,080 It's pretty inefficient, and we established that before. 75 00:05:01,080 --> 00:05:04,330 So we could make this more efficient with dynamic 76 00:05:04,330 --> 00:05:05,580 programming, right? 77 00:05:07,660 --> 00:05:10,950 Let's draw out the call tree for f of 5. 78 00:05:29,420 --> 00:05:30,670 Doing a lot of writing. 79 00:05:32,960 --> 00:05:34,940 OK. 80 00:05:34,940 --> 00:05:40,870 So if you look at this, we see that in a lot of places, we're 81 00:05:40,870 --> 00:05:42,740 repeating computations. 82 00:05:42,740 --> 00:05:45,860 So we repeat f of 2 three times. 83 00:05:48,970 --> 00:05:53,305 We also repeat f of 3, this computation, twice. 84 00:05:59,650 --> 00:06:02,260 The idea behind dynamic programming is that instead of 85 00:06:02,260 --> 00:06:06,150 repeating these computations over and over again, we're 86 00:06:06,150 --> 00:06:07,840 just going to compute them once. 87 00:06:07,840 --> 00:06:12,420 So if we look at the implementation of this 88 00:06:12,420 --> 00:06:16,930 recursive Fibonacci, we see that the first recursive call 89 00:06:16,930 --> 00:06:18,720 is going down this side of the tree. 90 00:06:21,990 --> 00:06:29,070 And so the upshot is that this-- 91 00:06:29,070 --> 00:06:34,450 all the values from f5, f4, f3, f2, f1, f0, they all get 92 00:06:34,450 --> 00:06:38,950 computed before any of these branches. 93 00:06:38,950 --> 00:06:46,250 So what we can do is instead of recomputing f2 and f2 here, 94 00:06:46,250 --> 00:06:47,670 we can just compute it here. 95 00:06:47,670 --> 00:06:53,320 Save off this value somewhere in some sort of a brain, and 96 00:06:53,320 --> 00:06:59,030 do the same thing for f3, and f4, but that's 97 00:06:59,030 --> 00:07:01,000 not going to matter. 98 00:07:01,000 --> 00:07:04,290 So when we get to the second recursive call, say when we're 99 00:07:04,290 --> 00:07:09,650 calling Fibonacci of 3, we compute f of 2, get f of 1, we 100 00:07:09,650 --> 00:07:13,150 come back up to Fibonacci of 4, right? 101 00:07:13,150 --> 00:07:16,600 The next recursive call is going to be to Fibonacci of 2. 102 00:07:16,600 --> 00:07:19,140 But since we've already computed it, and we've saved 103 00:07:19,140 --> 00:07:22,250 it off in a brain, we can just look up this value instead of 104 00:07:22,250 --> 00:07:24,790 doing all the computation again. 105 00:07:24,790 --> 00:07:28,440 So when you have a very small tree like this, right, it's 106 00:07:28,440 --> 00:07:29,260 not a huge benefit. 107 00:07:29,260 --> 00:07:33,780 But if you have like a lot, like f of 100, this is going 108 00:07:33,780 --> 00:07:35,920 to make a huge difference. 109 00:07:35,920 --> 00:07:39,114 Is anyone lost? 110 00:07:39,114 --> 00:07:40,364 OK. 111 00:07:43,360 --> 00:07:44,780 So why don't we take a look-- 112 00:07:52,760 --> 00:07:59,870 So this is an implementation of Fib that has an extra 113 00:07:59,870 --> 00:08:01,700 parameter called memo. 114 00:08:01,700 --> 00:08:03,790 And memo is the brain I'm talking about. 115 00:08:03,790 --> 00:08:07,070 This is where we're going to stash those computed values so 116 00:08:07,070 --> 00:08:10,330 we don't have to compute them again. 117 00:08:10,330 --> 00:08:16,760 And when it initially comes in, if memo is none, that is, 118 00:08:16,760 --> 00:08:19,400 we haven't passed in a dictionary or if it's like the 119 00:08:19,400 --> 00:08:23,720 first call to this function, then it's going to set a key 120 00:08:23,720 --> 00:08:26,510 of 0 to 0 and key of 1 to 1. 121 00:08:26,510 --> 00:08:29,990 Those correspond to f of 0 and f of 1, right, actually. 122 00:08:36,429 --> 00:08:39,870 No one pointed that out? 123 00:08:39,870 --> 00:08:42,559 All right. 124 00:08:42,559 --> 00:08:46,030 So, now it's going to-- 125 00:08:46,030 --> 00:08:48,180 after it does that initialization, it's going to 126 00:08:48,180 --> 00:08:52,020 come down to this if statement, and it's going to 127 00:08:52,020 --> 00:08:55,840 check for whether or not N is in memo. 128 00:08:55,840 --> 00:08:59,170 What this is actually asking is, have we 129 00:08:59,170 --> 00:09:01,530 seen this number before? 130 00:09:01,530 --> 00:09:05,580 When we've been computing this number, the big Fibonacci 131 00:09:05,580 --> 00:09:08,640 number, right? 132 00:09:08,640 --> 00:09:13,540 So if it's down here, and it's looking at f of 2, it's 133 00:09:13,540 --> 00:09:16,340 asking, have I seen Fibonacci of 2 when I've done this 134 00:09:16,340 --> 00:09:18,020 computation before? 135 00:09:18,020 --> 00:09:20,870 And if it's in this tree, then the answer is yes because it's 136 00:09:20,870 --> 00:09:22,120 seen it down here. 137 00:09:24,170 --> 00:09:27,750 If it hasn't seen it though, it's got to do the work. 138 00:09:27,750 --> 00:09:31,920 So it's got to actually do the recursive calls. 139 00:09:31,920 --> 00:09:35,400 And this is when it travels down this left 140 00:09:35,400 --> 00:09:37,620 branch of the tree. 141 00:09:37,620 --> 00:09:38,870 Does that makes sense? 142 00:09:41,090 --> 00:09:42,995 And all we're going to do is store it off here. 143 00:09:45,960 --> 00:09:47,200 And then at the end, we just return 144 00:09:47,200 --> 00:09:50,190 whatever's in our memory. 145 00:09:50,190 --> 00:09:52,370 So why don't we take a look at how this runs. 146 00:10:00,920 --> 00:10:07,720 So remember, this is 1.6 million with the old 147 00:10:07,720 --> 00:10:11,540 implementation, and now it's only 57. 148 00:10:11,540 --> 00:10:19,290 And in fact, it's linear and exactly 2 to the N minus 1, or 149 00:10:19,290 --> 00:10:20,540 2 times N minus 1. 150 00:10:23,730 --> 00:10:25,980 Everyone follow that? 151 00:10:25,980 --> 00:10:26,370 Any questions? 152 00:10:26,370 --> 00:10:29,370 AUDIENCE: There's not too much we could do. 153 00:10:29,370 --> 00:10:31,390 PROFESSOR: No. 154 00:10:31,390 --> 00:10:34,130 It saves a lot of work for you. 155 00:10:34,130 --> 00:10:35,300 Because as soon as it-- 156 00:10:35,300 --> 00:10:39,890 so let's say that it's computed f of 4, this entire 157 00:10:39,890 --> 00:10:42,030 sub-tree here. 158 00:10:42,030 --> 00:10:45,090 As soon as it sees f of 3, it's going to look in it's 159 00:10:45,090 --> 00:10:48,110 brain, and you've already seen f of 3, and it says, I've 160 00:10:48,110 --> 00:10:50,590 already seen this, so I don't need to do all this 161 00:10:50,590 --> 00:10:51,740 computation here. 162 00:10:51,740 --> 00:10:53,810 I don't need to do all these recursive calls. 163 00:10:53,810 --> 00:10:56,670 I just have to return whatever's in the dictionary. 164 00:10:56,670 --> 00:10:57,940 That's where your savings come in. 165 00:11:01,890 --> 00:11:05,690 So let's take a look at another example. 166 00:11:09,070 --> 00:11:13,590 The point is, you can have some really huge savings if 167 00:11:13,590 --> 00:11:16,360 you write your code right. 168 00:11:16,360 --> 00:11:19,530 So let's look at a different problem. 169 00:11:19,530 --> 00:11:21,830 Let's say that I have a robot. 170 00:11:30,290 --> 00:11:32,520 And this robot is positioned on the grid. 171 00:11:36,510 --> 00:11:54,060 Let's say that it has N rows, and N columns, or M columns. 172 00:11:56,660 --> 00:12:01,670 Your robot is starting out here, and it 173 00:12:01,670 --> 00:12:03,660 wants to get here. 174 00:12:03,660 --> 00:12:07,850 Your robot though is very stupid, and it can go only 175 00:12:07,850 --> 00:12:10,750 down and to the right. 176 00:12:10,750 --> 00:12:15,240 The question is, how many unique paths are there from 177 00:12:15,240 --> 00:12:18,770 the top left square to the bottom right square, given 178 00:12:18,770 --> 00:12:20,020 those constraints? 179 00:12:26,430 --> 00:12:28,650 If you try and do this analytically, you'll probably 180 00:12:28,650 --> 00:12:29,870 hurt yourself. 181 00:12:29,870 --> 00:12:34,900 The easier way to do it, or at least I think so, is to 182 00:12:34,900 --> 00:12:37,970 realize that in order to get to g, there's only two places 183 00:12:37,970 --> 00:12:38,660 that it can come from. 184 00:12:38,660 --> 00:12:43,730 It can come from here, it can come from here, right. 185 00:12:43,730 --> 00:12:47,780 So the total number of unique paths coming into g are the 186 00:12:47,780 --> 00:12:50,620 total number of unique paths coming into this guy, and the 187 00:12:50,620 --> 00:12:52,760 total number of unique paths coming into this guy. 188 00:12:52,760 --> 00:12:55,880 So there's your overlapping sub-problems, right, and also 189 00:12:55,880 --> 00:12:56,970 your optimal substructure. 190 00:12:56,970 --> 00:12:59,890 If I can figure out these numbers, then I can figure out 191 00:12:59,890 --> 00:13:02,840 this number, right. 192 00:13:02,840 --> 00:13:04,090 So-- 193 00:13:07,570 --> 00:13:10,010 and then these guys, the same condition applies. 194 00:13:10,010 --> 00:13:11,380 If I know these two numbers, then I can 195 00:13:11,380 --> 00:13:13,230 figure out this number. 196 00:13:13,230 --> 00:13:14,420 If I know these two numbers, then I can 197 00:13:14,420 --> 00:13:16,320 figure out this number. 198 00:13:16,320 --> 00:13:21,046 So one implementation-- 199 00:13:21,046 --> 00:13:23,890 well, one other thing. 200 00:13:23,890 --> 00:13:28,940 If I get down to this case where I have a 1 by M grid, 201 00:13:28,940 --> 00:13:30,440 how many different ways are there to get 202 00:13:30,440 --> 00:13:33,090 from here to here? 203 00:13:33,090 --> 00:13:34,340 One, right? 204 00:13:37,230 --> 00:13:40,130 And then same thing for M by N right? 205 00:13:44,980 --> 00:13:49,260 So the first crack at this, here's a recursive function. 206 00:13:52,630 --> 00:13:56,400 All we're going to do is, if we only have 1 row or 1 207 00:13:56,400 --> 00:13:59,850 column, we return 1. 208 00:13:59,850 --> 00:14:04,160 Otherwise, we're going to look for the number of robot paths 209 00:14:04,160 --> 00:14:08,910 in an N minus 1 by M matrix and then the number of paths 210 00:14:08,910 --> 00:14:11,140 in an N by M minus 1 matrix. 211 00:14:13,890 --> 00:14:15,245 So let's take a look. 212 00:14:21,450 --> 00:14:27,410 We're going to do this on a 14 by 14 grid. 213 00:14:27,410 --> 00:14:29,230 And this will take a few seconds. 214 00:14:40,550 --> 00:14:40,736 OK. 215 00:14:40,736 --> 00:14:45,460 So there's a lot. 216 00:14:45,460 --> 00:14:49,560 10 million unique paths, and it took about 20 million steps 217 00:14:49,560 --> 00:14:52,050 to figure it out. 218 00:14:52,050 --> 00:14:56,570 So we're going to pull the same trick for this problem 219 00:14:56,570 --> 00:14:59,450 that we did for Fibonacci. 220 00:14:59,450 --> 00:15:05,080 We're going to memorize, or memoize, the different paths 221 00:15:05,080 --> 00:15:10,750 or the different solutions, except our key is going to be 222 00:15:10,750 --> 00:15:11,520 a little different. 223 00:15:11,520 --> 00:15:14,360 It's just going to be N and M. 224 00:15:14,360 --> 00:15:18,130 So again, if we have-- 225 00:15:18,130 --> 00:15:20,790 well, again, if N and M is not memo, then we 226 00:15:20,790 --> 00:15:22,720 need to compute it. 227 00:15:22,720 --> 00:15:27,750 If either is 1, then we remember it as 1. 228 00:15:27,750 --> 00:15:32,950 And if they're both greater than 1, then we're going to 229 00:15:32,950 --> 00:15:37,950 look at the number of paths in N minus 1 by M, and N 230 00:15:37,950 --> 00:15:41,100 times M minus 1. 231 00:15:41,100 --> 00:15:43,480 And then we also know that the solutions 232 00:15:43,480 --> 00:15:46,030 are symmetric, right. 233 00:15:46,030 --> 00:15:50,780 So it's going to be the same for an N by M and an M by N. 234 00:15:50,780 --> 00:15:52,220 And just return the solution. 235 00:15:52,220 --> 00:15:53,870 So let's try this out. 236 00:16:00,880 --> 00:16:04,380 We get the same answer, but it only takes 104 steps to do it. 237 00:16:04,380 --> 00:16:09,620 So it's a pretty huge savings there. 238 00:16:09,620 --> 00:16:11,890 So the other-- 239 00:16:11,890 --> 00:16:13,930 this is an example of a top-down 240 00:16:13,930 --> 00:16:15,710 dynamic programming solution. 241 00:16:15,710 --> 00:16:19,230 We looked at the big problem, and we broke it down into two 242 00:16:19,230 --> 00:16:20,430 smaller problems. 243 00:16:20,430 --> 00:16:22,320 We looked at those two smaller sub-problems, we broke them 244 00:16:22,320 --> 00:16:27,630 down into two smaller sub-problems a piece. 245 00:16:27,630 --> 00:16:29,245 We can also go from the bottom up. 246 00:16:32,210 --> 00:16:34,080 And we might want to do this for a reason that 247 00:16:34,080 --> 00:16:35,940 I'll show in a second. 248 00:16:35,940 --> 00:16:38,860 So I think I needed that. 249 00:16:50,000 --> 00:16:51,700 This is going to go from the bottom up. 250 00:16:51,700 --> 00:16:54,610 So instead of starting back here and asking how many ways 251 00:16:54,610 --> 00:16:59,010 I can get from these two guys, it's going to start at the 252 00:16:59,010 --> 00:17:01,690 beginning and ask how many ways I can get here. 253 00:17:01,690 --> 00:17:03,380 One, right. 254 00:17:03,380 --> 00:17:11,770 And then here let's imagine that we only have a 2 by 2. 255 00:17:11,770 --> 00:17:17,020 It's going to look at, again, the number of ways to get to 256 00:17:17,020 --> 00:17:19,790 the square to the left and to the square to the 257 00:17:19,790 --> 00:17:22,849 top, and add it here. 258 00:17:22,849 --> 00:17:23,450 Add it here. 259 00:17:23,450 --> 00:17:25,140 So what we're doing here-- 260 00:17:25,140 --> 00:17:31,310 what we're doing in this version is we're growing a 261 00:17:31,310 --> 00:17:34,030 grid towards a solution. 262 00:17:37,970 --> 00:17:39,220 Does that make sense? 263 00:17:43,530 --> 00:17:46,700 This just sets up a matrix. 264 00:17:46,700 --> 00:17:49,520 The top row is going to be initialized to 1. 265 00:17:49,520 --> 00:17:51,110 First column is going to be 1. 266 00:17:54,240 --> 00:18:01,660 And then for everything else, we're just going to add the 267 00:18:01,660 --> 00:18:04,405 row above and the column to the left. 268 00:18:08,530 --> 00:18:12,940 And return whatever's down in this lower right corner. 269 00:18:12,940 --> 00:18:16,620 So it gets the same solution, just in a different direction. 270 00:18:19,670 --> 00:18:32,290 So now you might want to do this because imagine if 271 00:18:32,290 --> 00:18:36,410 instead of 14, we had 1,400. 272 00:18:36,410 --> 00:18:38,260 So this should crash. 273 00:18:42,730 --> 00:18:46,290 Python has a maximum recursion depth. 274 00:18:46,290 --> 00:18:48,210 If you have too many recursive columns, it's going to tell 275 00:18:48,210 --> 00:18:51,750 you, I can't go that deep, and kick you out. 276 00:18:51,750 --> 00:18:58,350 So I have 1,400 rows and 1,400 columns, 277 00:18:58,350 --> 00:18:59,600 that's a lot of recursion. 278 00:19:10,720 --> 00:19:22,800 But if we do it iteratively, where we have no recursion, 279 00:19:22,800 --> 00:19:27,870 that's the number of unique paths, which is a pretty 280 00:19:27,870 --> 00:19:30,890 sizable number. 281 00:19:30,890 --> 00:19:35,380 And that's the number of columns we made, which for a 282 00:19:35,380 --> 00:19:39,040 1,400 by 1,400 matrix is not too bad, right. 283 00:19:43,700 --> 00:19:43,786 All right. 284 00:19:43,786 --> 00:19:44,850 So that was an easy one. 285 00:19:44,850 --> 00:19:46,646 So now we're going to go to little harder one. 286 00:19:51,730 --> 00:19:54,190 Everyone doing all right? 287 00:19:54,190 --> 00:19:55,440 OK. 288 00:19:58,740 --> 00:20:02,460 This is a counting change problem. 289 00:20:02,460 --> 00:20:19,170 The idea is, let's assume I have a currency with coins of 290 00:20:19,170 --> 00:20:20,470 a certain value. 291 00:20:23,980 --> 00:20:27,850 I'm not sure where I got the $0.27 from, but that's 292 00:20:27,850 --> 00:20:31,520 apparently the value for the coins in our currency. 293 00:20:31,520 --> 00:20:34,770 So the problem is-- 294 00:20:34,770 --> 00:20:36,790 let's say that I have a sum total-- 295 00:20:39,890 --> 00:20:42,590 the question is, how many different combinations of 296 00:20:42,590 --> 00:20:46,790 coins are there that equal total? 297 00:20:53,300 --> 00:20:56,090 The way to break this down is-- so does everyone 298 00:20:56,090 --> 00:20:57,710 understand the problem? 299 00:20:57,710 --> 00:21:01,710 So like if I say I want to give change $0.41 to a 300 00:21:01,710 --> 00:21:04,725 customer, then it's like a quarter, a dime, a nickel, and 301 00:21:04,725 --> 00:21:05,975 a penny, right? 302 00:21:11,380 --> 00:21:14,670 We can think of the problem in two ways. 303 00:21:14,670 --> 00:21:18,660 Well, we can break the problem down into two sub-problems by 304 00:21:18,660 --> 00:21:28,800 first considering what the total is-- what the number of 305 00:21:28,800 --> 00:21:34,880 combos would be if I use the largest coin in my set. 306 00:21:38,920 --> 00:21:51,550 We're using at least one of the largest coins. 307 00:21:51,550 --> 00:22:05,600 And then the other is the number of combinations if not 308 00:22:05,600 --> 00:22:10,190 using the largest coin. 309 00:22:13,040 --> 00:22:14,290 OK? 310 00:22:16,030 --> 00:22:19,880 So this turned, actually turns, into a nicer 311 00:22:19,880 --> 00:22:28,850 sub-problem, which is if I have total minus largest-- 312 00:22:28,850 --> 00:22:37,960 so in this case, minus $0.27, what's the number of 313 00:22:37,960 --> 00:22:44,590 combinations for this sub-problem? 314 00:22:44,590 --> 00:22:45,840 You follow? 315 00:22:47,540 --> 00:22:52,890 And then this guy can be formulated as the number-- 316 00:22:52,890 --> 00:23:00,316 so I still have total number of combinations. 317 00:23:03,930 --> 00:23:10,490 But then I take out the largest coin, so coins is now, 318 00:23:10,490 --> 00:23:16,590 instead of 1, instead of having $0.27 as the largest 319 00:23:16,590 --> 00:23:20,330 coin, now has $0.25 as the largest coin. 320 00:23:20,330 --> 00:23:21,580 Does that makes sense to people? 321 00:23:24,320 --> 00:23:30,780 And so the solution here to this big problem is the 322 00:23:30,780 --> 00:23:32,310 solution to-- 323 00:23:32,310 --> 00:23:33,570 or is the sum of the solutions to this 324 00:23:33,570 --> 00:23:36,490 problem and In this problem. 325 00:23:36,490 --> 00:23:39,000 Make sense? 326 00:23:39,000 --> 00:23:41,360 All right. 327 00:23:41,360 --> 00:23:46,450 Why don't we take a look at the first version of this 328 00:23:46,450 --> 00:23:50,270 problem or this implementation. 329 00:23:50,270 --> 00:23:56,080 So we have three base cases. 330 00:23:56,080 --> 00:23:59,110 So the first base case is-- 331 00:23:59,110 --> 00:24:01,610 well, first let me explain the function. 332 00:24:01,610 --> 00:24:07,100 So, total, that's the amount that we want. 333 00:24:07,100 --> 00:24:08,080 Coins-- 334 00:24:08,080 --> 00:24:09,730 it's the set of coins in our currency. 335 00:24:12,310 --> 00:24:20,950 And I only have $0.05, $0.10, $0.25, and $0.27, so, oh well. 336 00:24:20,950 --> 00:24:24,290 The first thing we do is check to see if total is 0. 337 00:24:24,290 --> 00:24:28,340 That means that we're trying to figure out what combination 338 00:24:28,340 --> 00:24:30,580 of coins is equal to 0. 339 00:24:30,580 --> 00:24:33,890 And of course, there's only one combination of coins. 340 00:24:33,890 --> 00:24:35,830 There are no coins. 341 00:24:35,830 --> 00:24:37,160 So that actually is 1. 342 00:24:40,610 --> 00:24:41,860 This case-- 343 00:24:43,810 --> 00:24:47,140 this is if we're trying a particular coin that's a 344 00:24:47,140 --> 00:24:50,640 little too large to fit into our total. 345 00:24:50,640 --> 00:24:52,660 So we wind up going below 0. 346 00:24:52,660 --> 00:24:55,340 That means we can't use that coin for this particular 347 00:24:55,340 --> 00:24:56,350 total, right. 348 00:24:56,350 --> 00:25:01,980 So there are no combinations that work for this. 349 00:25:01,980 --> 00:25:07,170 And then this last case is, if we're no longer using-- 350 00:25:07,170 --> 00:25:10,820 if we have no more coins, and we still have stuff that we 351 00:25:10,820 --> 00:25:16,430 need to make up, like we still have some value in total, then 352 00:25:16,430 --> 00:25:20,950 that means that this particular combination that 353 00:25:20,950 --> 00:25:24,180 we're trying out also doesn't work, so we return 0. 354 00:25:24,180 --> 00:25:31,470 So that's this case, where we've taken out everything, so 355 00:25:31,470 --> 00:25:35,500 our set of coins is the empty set, is nothing. 356 00:25:35,500 --> 00:25:36,750 Does that make sense? 357 00:25:41,420 --> 00:25:48,300 So our first recursive call is looking for the number of ways 358 00:25:48,300 --> 00:25:49,550 without the last coin. 359 00:25:52,290 --> 00:25:54,870 That's this case. 360 00:25:54,870 --> 00:25:56,650 And all we're doing is we're passing in the total we got 361 00:25:56,650 --> 00:26:00,170 before, and all the coins except for the largest one, so 362 00:26:00,170 --> 00:26:01,230 the last one. 363 00:26:01,230 --> 00:26:03,330 So we're stricken one off for each recursive call. 364 00:26:07,200 --> 00:26:11,350 And then the second case here is if we do use at least one 365 00:26:11,350 --> 00:26:13,560 of the largest coins. 366 00:26:13,560 --> 00:26:14,990 In this case, we're just going to subtract 367 00:26:14,990 --> 00:26:17,530 that off the total. 368 00:26:17,530 --> 00:26:21,790 And we're going to pass in coins as is. 369 00:26:21,790 --> 00:26:23,040 Make sense? 370 00:26:25,120 --> 00:26:26,380 And then we just return the sum. 371 00:26:43,780 --> 00:26:44,210 OK. 372 00:26:44,210 --> 00:26:47,120 So not too bad. 373 00:26:47,120 --> 00:26:50,280 Everyone follow that code? 374 00:26:50,280 --> 00:26:52,340 All right. 375 00:26:52,340 --> 00:26:58,480 So this is without doing any memoization, it's just using 376 00:26:58,480 --> 00:27:01,060 recursion, breaking it down into two sub-problems and 377 00:27:01,060 --> 00:27:04,450 figuring out a solution. 378 00:27:04,450 --> 00:27:06,400 But now we're going to use dynamic programming to 379 00:27:06,400 --> 00:27:09,720 optimize this a little bit. 380 00:27:09,720 --> 00:27:13,080 So in this case, we have our little memoization 381 00:27:13,080 --> 00:27:14,970 dictionary-- 382 00:27:14,970 --> 00:27:16,220 you notice a pattern here? 383 00:27:19,940 --> 00:27:20,758 Yeah? 384 00:27:20,758 --> 00:27:22,221 AUDIENCE: Do you have an infinite 385 00:27:22,221 --> 00:27:23,880 amount to supply those? 386 00:27:23,880 --> 00:27:24,770 PROFESSOR: Yes. 387 00:27:24,770 --> 00:27:27,690 So you're assuming that you have an infinite number of 388 00:27:27,690 --> 00:27:31,308 $0.27 cent pieces, $0.25 cent pieces, et cetera. 389 00:27:31,308 --> 00:27:34,272 AUDIENCE: [UNINTELLIGIBLE] 390 00:27:34,272 --> 00:27:35,754 just add 1's [UNINTELLIGIBLE] 391 00:27:40,694 --> 00:27:43,220 PROFESSOR: And it's possible to get below 0. 392 00:27:50,070 --> 00:27:52,380 So let's see. 393 00:27:52,380 --> 00:27:54,060 This is all the same, right? 394 00:27:54,060 --> 00:27:55,310 No surprises there. 395 00:27:57,500 --> 00:28:02,160 But then what we're going to do is we're going to memoize 396 00:28:02,160 --> 00:28:06,040 on the total that we're trying to find and the largest 397 00:28:06,040 --> 00:28:11,140 value-- largest currency piece that we have. 398 00:28:11,140 --> 00:28:15,460 And if it's not in our dictionary, then we're going 399 00:28:15,460 --> 00:28:16,710 to compute it. 400 00:28:22,340 --> 00:28:23,970 And then once we get the solution, we're going to 401 00:28:23,970 --> 00:28:29,470 memoize it and return it. 402 00:28:29,470 --> 00:28:30,720 So let's see how this runs. 403 00:28:40,510 --> 00:28:41,760 It could've been by 4. 404 00:28:45,590 --> 00:28:46,840 That make sense to everyone? 405 00:28:49,880 --> 00:28:52,470 No questions? 406 00:28:52,470 --> 00:28:53,720 Wow. 407 00:28:56,740 --> 00:28:58,190 So now let's try a different formulation. 408 00:29:00,750 --> 00:29:03,835 This one's a little tricky. 409 00:29:08,860 --> 00:29:10,546 Let's imagine that I have a grid. 410 00:29:13,190 --> 00:29:18,375 And down the rows I have numbers up to total. 411 00:29:21,420 --> 00:29:22,670 OK, starting from 0. 412 00:29:29,340 --> 00:29:31,840 Across the columns, I have my different currency pieces. 413 00:29:45,440 --> 00:29:48,080 So the first two implementations we're going 414 00:29:48,080 --> 00:29:49,936 top down, now we're going bottom up. 415 00:29:52,920 --> 00:29:59,440 The way you read this is, if I have a total of 0, this is a 416 00:29:59,440 --> 00:30:05,785 number and my largest currency piece is $0.05, or $0.10, or 417 00:30:05,785 --> 00:30:10,220 $0.25, or $0.27, it's the number of coin combinations I 418 00:30:10,220 --> 00:30:13,090 have that will equal this total, right. 419 00:30:13,090 --> 00:30:20,560 So in this first row, that's my base case, right. 420 00:30:23,060 --> 00:30:28,820 And then down here, we know this is all going to be 0, 421 00:30:28,820 --> 00:30:30,780 because if I have no coin pieces-- 422 00:30:35,170 --> 00:30:39,260 So now, the bottom-up way is, we're going to fill in this 423 00:30:39,260 --> 00:30:40,510 table here. 424 00:30:44,080 --> 00:30:48,090 So we're just going to iterate through all the totals that 425 00:30:48,090 --> 00:30:49,340 are possible. 426 00:30:51,590 --> 00:30:54,250 And for each total, we're going to iterate through all 427 00:30:54,250 --> 00:30:55,500 the largest coin denominations. 428 00:30:58,370 --> 00:31:00,840 Right? 429 00:31:00,840 --> 00:31:04,710 And then we're just going to do two checks. 430 00:31:04,710 --> 00:31:06,540 We're going to look at the current total we're looking 431 00:31:06,540 --> 00:31:13,460 at, so let's say that we're on row one, so our total is 1. 432 00:31:13,460 --> 00:31:17,930 And we're going to subtract the largest denomination of 433 00:31:17,930 --> 00:31:21,270 currency that we're looking at. 434 00:31:21,270 --> 00:31:24,430 We start out here. 435 00:31:24,430 --> 00:31:29,610 So we're going to subtract 5 from total of 1. 436 00:31:29,610 --> 00:31:34,870 And that's less than 0. 437 00:31:34,870 --> 00:31:39,630 And when it's less than 0, all we're going to do is carry the 438 00:31:39,630 --> 00:31:46,690 number of combinations from the current total to the next 439 00:31:46,690 --> 00:31:48,630 smallest largest denomination. 440 00:31:48,630 --> 00:31:52,750 So if we're at 5 and we know we're less than 0, that means 441 00:31:52,750 --> 00:31:56,090 we're going to carry this value over. 442 00:31:56,090 --> 00:31:57,060 And we're now going to do the same thing 443 00:31:57,060 --> 00:31:58,310 for all these values. 444 00:32:00,380 --> 00:32:02,460 Now let's get to an interesting case. 445 00:32:02,460 --> 00:32:17,700 So now we're looking at a total of 5, OK? 446 00:32:22,620 --> 00:32:27,240 When our largest coin is $0.05 and we subtract it from a 447 00:32:27,240 --> 00:32:30,550 total of 5, that's not less than 0, right? 448 00:32:30,550 --> 00:32:31,590 So that means we're going to go into the 449 00:32:31,590 --> 00:32:35,370 second branch of f statement. 450 00:32:35,370 --> 00:32:36,540 So again, the same thing. 451 00:32:36,540 --> 00:32:39,750 We're going to look at the number of ways with the 452 00:32:39,750 --> 00:32:41,930 largest coin. 453 00:32:41,930 --> 00:32:44,790 And to do that, we're going to use our table. 454 00:32:44,790 --> 00:32:50,790 We're going to look at the current total, which is 5, 455 00:32:50,790 --> 00:32:53,080 minus the largest coin that we're looking 456 00:32:53,080 --> 00:32:55,530 at, which is $0.05. 457 00:32:55,530 --> 00:32:59,620 So that's going to index us into this row because 458 00:32:59,620 --> 00:33:02,520 5 minus 5 is 0. 459 00:33:02,520 --> 00:33:04,910 And then we're going to look at the current 460 00:33:04,910 --> 00:33:08,280 coin, which is $0.05. 461 00:33:08,280 --> 00:33:15,330 So that's going to be 1, right? 462 00:33:15,330 --> 00:33:16,900 Well, I skipped a step. 463 00:33:16,900 --> 00:33:19,020 So we're going to get this value, right, so this is the 464 00:33:19,020 --> 00:33:21,900 number of ways with the largest coin. 465 00:33:24,840 --> 00:33:26,910 And then we're going to look at the number of ways without 466 00:33:26,910 --> 00:33:30,070 the last coin, so if this is the largest coin that we're 467 00:33:30,070 --> 00:33:36,000 looking at, then without it, this is all we have. 468 00:33:36,000 --> 00:33:37,730 So that's going to be 0. 469 00:33:37,730 --> 00:33:39,440 So 0 plus 1, that's 1. 470 00:33:43,860 --> 00:33:46,740 And then for 10-- 471 00:33:46,740 --> 00:33:53,150 so again, this becomes 0. 472 00:33:53,150 --> 00:33:56,580 So the next interesting one is going to be 10. 473 00:33:56,580 --> 00:33:58,220 Is everyone following so far what we're doing? 474 00:34:01,060 --> 00:34:02,310 This would be 0. 475 00:34:04,780 --> 00:34:09,409 This is going to be 1 plus 0. 476 00:34:13,070 --> 00:34:16,290 And this is going to be 1 plus 1. 477 00:34:20,340 --> 00:34:22,670 Making sense? 478 00:34:22,670 --> 00:34:23,690 Make an error? 479 00:34:23,690 --> 00:34:25,400 No, OK. 480 00:34:25,400 --> 00:34:30,290 And then this is going to be 0 Everyone get the idea? 481 00:34:30,290 --> 00:34:31,874 So we're just filling in this table. 482 00:34:40,179 --> 00:34:41,432 What's that? 483 00:34:41,432 --> 00:34:44,834 AUDIENCE: [INAUDIBLE] 484 00:34:44,834 --> 00:34:46,778 PROFESSOR: Here? 485 00:34:46,778 --> 00:34:50,666 AUDIENCE: There's only one way to make 10 plus 5. 486 00:34:50,666 --> 00:34:52,060 PROFESSOR: Because-- 487 00:34:52,060 --> 00:34:53,480 the smallest-- 488 00:34:53,480 --> 00:34:58,530 so if I had 1 here, like so I was using a one piece, then 489 00:34:58,530 --> 00:35:01,590 this would be 2. 490 00:35:01,590 --> 00:35:04,440 But because we only have 5, there's only 491 00:35:04,440 --> 00:35:07,010 one way to make 10. 492 00:35:07,010 --> 00:35:09,366 Two 5's. 493 00:35:09,366 --> 00:35:12,234 Make sense? 494 00:35:12,234 --> 00:35:13,668 AUDIENCE: [UNINTELLIGIBLE] 495 00:35:13,668 --> 00:35:17,510 because you can either use one 10 or two 5's. 496 00:35:17,510 --> 00:35:19,894 PROFESSOR: Yes, exactly. 497 00:35:19,894 --> 00:35:24,250 AUDIENCE: So if we're using the table that as-- 498 00:35:24,250 --> 00:35:28,122 so you have $0.05 as the largest coin, so there's only 499 00:35:28,122 --> 00:35:30,542 one way of doing it using $0.05 as the largest coin? 500 00:35:30,542 --> 00:35:33,446 How come that 1 doesn't go all the way across the row? 501 00:35:33,446 --> 00:35:35,020 Because if you have $0.25 as the largest coin 502 00:35:35,020 --> 00:35:37,580 you could use $0.05's. 503 00:35:37,580 --> 00:35:38,830 PROFESSOR: You know what, you're right. 504 00:35:41,804 --> 00:35:43,054 Made an error. 505 00:35:49,289 --> 00:35:52,283 Actually, that would be 0. 506 00:35:57,772 --> 00:35:59,768 AUDIENCE: [INAUDIBLE] 507 00:35:59,768 --> 00:36:03,261 PROFESSOR: What's that? 508 00:36:03,261 --> 00:36:04,511 Yeah, but-- 509 00:36:11,744 --> 00:36:13,740 what can I say? 510 00:36:13,740 --> 00:36:14,990 It can be confusing. 511 00:36:17,290 --> 00:36:20,328 To prove that this crazy thing works, let's run it. 512 00:36:29,020 --> 00:36:33,830 So let's expand this. 513 00:36:33,830 --> 00:36:35,080 Actually, I'm going to use this. 514 00:36:38,480 --> 00:36:40,190 So all three versions-- 515 00:36:47,340 --> 00:36:49,260 what, did I make a mistake? 516 00:36:49,260 --> 00:36:51,635 AUDIENCE: No, it's just how you expanded the window. 517 00:36:51,635 --> 00:36:55,820 PROFESSOR: Oh, you like that? 518 00:36:55,820 --> 00:36:57,748 AUDIENCE: That's what we-- 519 00:36:57,748 --> 00:37:02,086 well, like Tracy's quote is always really, really small. 520 00:37:02,086 --> 00:37:02,580 But. 521 00:37:02,580 --> 00:37:03,830 AUDIENCE: No worries. 522 00:37:06,206 --> 00:37:08,370 PROFESSOR: It's a program called Divvy, if you're 523 00:37:08,370 --> 00:37:09,150 interested. 524 00:37:09,150 --> 00:37:12,040 I think it's like $10 on the Apps store or something, or 525 00:37:12,040 --> 00:37:14,125 maybe it's $5.00. 526 00:37:14,125 --> 00:37:17,760 It just allows you to do those little grid things, so if you 527 00:37:17,760 --> 00:37:19,240 have lots of windows and stuff-- 528 00:37:22,410 --> 00:37:24,360 and if you need to increase your font size, that's a 529 00:37:24,360 --> 00:37:27,641 little bit more involved with Python. 530 00:37:27,641 --> 00:37:30,240 Anyway, so they all give the same output except for the 531 00:37:30,240 --> 00:37:34,140 number of steps that they take, so the initial one took 532 00:37:34,140 --> 00:37:38,100 855, the one with the memoization took 209 steps, 533 00:37:38,100 --> 00:37:41,770 and the one without memoization but from bottom up 534 00:37:41,770 --> 00:37:44,010 took only 337 steps, so-- 535 00:37:48,170 --> 00:37:50,700 it's just-- it's three different ways of attacking 536 00:37:50,700 --> 00:37:53,110 the same problem. 537 00:37:53,110 --> 00:37:57,960 And objectively, the last two are better than the first one. 538 00:38:00,820 --> 00:38:04,650 We might think of cases where we would want to use say the 539 00:38:04,650 --> 00:38:08,010 table-based method over their recursion method. 540 00:38:08,010 --> 00:38:11,740 Like if we had like lots of combinations to explore or 541 00:38:11,740 --> 00:38:14,690 some kind of situation where you got into a really deep 542 00:38:14,690 --> 00:38:18,040 recursion that was too deep for Python to handle and we 543 00:38:18,040 --> 00:38:21,250 got kicked out like we did for the number of robot paths. 544 00:38:21,250 --> 00:38:24,590 So that would be kind of like a criteria for which algorithm 545 00:38:24,590 --> 00:38:25,840 you would choose over the other.