1 00:00:00,050 --> 00:00:01,770 The following content is provided 2 00:00:01,770 --> 00:00:04,010 under a Creative Commons license. 3 00:00:04,010 --> 00:00:06,860 Your support will help MIT OpenCourseWare continue 4 00:00:06,860 --> 00:00:10,720 to offer high quality educational resources for free. 5 00:00:10,720 --> 00:00:13,320 To make a donation or view additional materials 6 00:00:13,320 --> 00:00:17,207 from hundreds of MIT courses, visit MIT OpenCourseWare 7 00:00:17,207 --> 00:00:17,832 at ocw.mit.edu. 8 00:00:21,130 --> 00:00:23,630 PROFESSOR: We're going to start a brand new, exciting topic, 9 00:00:23,630 --> 00:00:25,287 dynamic programming. 10 00:00:25,287 --> 00:00:25,870 AUDIENCE: Yes! 11 00:00:25,870 --> 00:00:26,830 PROFESSOR: Yeah! 12 00:00:26,830 --> 00:00:28,245 So exciting. 13 00:00:28,245 --> 00:00:30,620 Actually, I am really excited because dynamic programming 14 00:00:30,620 --> 00:00:33,820 is my favorite thing in the world, in algorithms. 15 00:00:33,820 --> 00:00:37,070 And it's going to be the next four lectures, 16 00:00:37,070 --> 00:00:38,640 it's so exciting. 17 00:00:38,640 --> 00:00:40,380 It has lots of different facets. 18 00:00:40,380 --> 00:00:44,720 It's a very general, powerful design technique. 19 00:00:44,720 --> 00:00:47,390 We don't talk a lot about algorithm design in this class, 20 00:00:47,390 --> 00:00:50,060 but dynamic programming is one that's so important. 21 00:00:50,060 --> 00:00:52,000 And also takes a little while to settle in. 22 00:00:52,000 --> 00:00:57,510 We like to injected it into you now, in 006. 23 00:00:57,510 --> 00:01:05,920 So in general, our motivation is designing new algorithms 24 00:01:05,920 --> 00:01:10,600 and dynamic programming, also called DP, 25 00:01:10,600 --> 00:01:17,840 is a great way-- or a very general, powerful way 26 00:01:17,840 --> 00:01:18,365 to do this. 27 00:01:33,860 --> 00:01:37,540 It's especially good, and intended for, optimization 28 00:01:37,540 --> 00:01:39,100 problems, things like shortest paths. 29 00:01:39,100 --> 00:01:41,274 You want to find the best way to do something. 30 00:01:41,274 --> 00:01:43,190 Shortest path is you want to find the shortest 31 00:01:43,190 --> 00:01:46,600 path, the minimum-length path. 32 00:01:46,600 --> 00:01:48,610 You want to minimize, maximize something, that's 33 00:01:48,610 --> 00:01:51,320 an optimization problem, and typically 34 00:01:51,320 --> 00:01:54,090 good algorithms to solve them involve dynamic programming. 35 00:01:54,090 --> 00:01:56,390 It's a bit of a broad statement. 36 00:01:56,390 --> 00:01:58,320 You can also think of dynamic programming 37 00:01:58,320 --> 00:02:01,700 as a kind of exhaustive search. 38 00:02:01,700 --> 00:02:03,270 Which is usually a bad thing to do 39 00:02:03,270 --> 00:02:05,260 because it leads to exponential time. 40 00:02:05,260 --> 00:02:09,449 But if you do it in a clever way, via dynamic programming, 41 00:02:09,449 --> 00:02:13,670 you typically get polynomial time. 42 00:02:13,670 --> 00:02:17,430 So one perspective is that dynamic programming 43 00:02:17,430 --> 00:02:23,170 is approximately careful brute force. 44 00:02:23,170 --> 00:02:25,650 It's kind of a funny combination. 45 00:02:25,650 --> 00:02:29,340 A bit of an oxymoron. 46 00:02:29,340 --> 00:02:31,600 But we take the idea of brute force, which is, 47 00:02:31,600 --> 00:02:35,690 try all possibilities and you do it carefully 48 00:02:35,690 --> 00:02:37,110 and you get it to polynomial time. 49 00:02:37,110 --> 00:02:38,985 There are a lot of problems where essentially 50 00:02:38,985 --> 00:02:41,650 the only known polynomial time algorithm is 51 00:02:41,650 --> 00:02:42,650 via dynamic programming. 52 00:02:42,650 --> 00:02:44,440 It doesn't always work, there's some problems 53 00:02:44,440 --> 00:02:46,856 where we don't think there are polynomial time algorithms, 54 00:02:46,856 --> 00:02:50,770 but when it's possible DP is a nice, sort of, 55 00:02:50,770 --> 00:02:54,037 general approach to it. 56 00:02:54,037 --> 00:02:56,620 And we're going to be talking a lot about dynamic programming. 57 00:02:56,620 --> 00:02:59,850 There's a lot of different ways to think about it. 58 00:02:59,850 --> 00:03:02,690 We'll look at a few today. 59 00:03:02,690 --> 00:03:05,754 We're going to warm up today with some fairly easy problems 60 00:03:05,754 --> 00:03:07,170 that we already know how to solve, 61 00:03:07,170 --> 00:03:09,690 namely computing Fibonacci numbers. 62 00:03:09,690 --> 00:03:11,210 It's pretty easy. 63 00:03:11,210 --> 00:03:13,092 And computing shortest paths. 64 00:03:13,092 --> 00:03:14,550 And then in the next three lectures 65 00:03:14,550 --> 00:03:16,530 we're going to get to more interesting examples 66 00:03:16,530 --> 00:03:20,210 where it's pretty surprising that you can even 67 00:03:20,210 --> 00:03:24,160 solve the problem in polynomial time. 68 00:03:24,160 --> 00:03:26,470 Probably the first burning question on your mind, 69 00:03:26,470 --> 00:03:29,280 though, is why is it called dynamic programming? 70 00:03:29,280 --> 00:03:31,240 What does that even mean? 71 00:03:31,240 --> 00:03:34,760 And I used to have this spiel about, well, you 72 00:03:34,760 --> 00:03:37,480 know, programming refers to the-- I think 73 00:03:37,480 --> 00:03:39,550 it's the British notion of the word, 74 00:03:39,550 --> 00:03:42,040 where it's about optimization. 75 00:03:42,040 --> 00:03:44,750 Optimization in American English is something 76 00:03:44,750 --> 00:03:47,690 like programming in British English, 77 00:03:47,690 --> 00:03:50,350 where you want to set up the program-- 78 00:03:50,350 --> 00:03:52,800 the schedule for your trains or something, 79 00:03:52,800 --> 00:03:54,850 where programming comes from originally. 80 00:03:54,850 --> 00:03:57,675 But I looked up the actual history of, 81 00:03:57,675 --> 00:04:00,730 why is it called dynamic programming. 82 00:04:00,730 --> 00:04:04,960 Dynamic programming was invented by a guy named Richard Bellman. 83 00:04:04,960 --> 00:04:07,850 You may have heard of Bellman in the Bellman-Ford algorithm. 84 00:04:07,850 --> 00:04:10,360 So this is actually the precursor to Bellman-Ford. 85 00:04:10,360 --> 00:04:13,290 And we're going to see Bellman-Ford come up naturally 86 00:04:13,290 --> 00:04:15,480 in this setting. 87 00:04:15,480 --> 00:04:17,519 So here's a quote about him. 88 00:04:17,519 --> 00:04:19,300 It says, Bellman explained that he 89 00:04:19,300 --> 00:04:22,250 invented the name dynamic programming to hide the fact 90 00:04:22,250 --> 00:04:25,840 that he was doing mathematical research. 91 00:04:25,840 --> 00:04:27,950 He was working at this place called Rand, 92 00:04:27,950 --> 00:04:31,890 and under a secretary of defense who had a pathological fear 93 00:04:31,890 --> 00:04:35,096 and hatred for the term research. 94 00:04:35,096 --> 00:04:36,970 So he settled on the term dynamic programming 95 00:04:36,970 --> 00:04:39,450 because it would be difficult to give 96 00:04:39,450 --> 00:04:42,000 a pejorative meaning to it. 97 00:04:42,000 --> 00:04:43,870 And because it was something not even 98 00:04:43,870 --> 00:04:45,830 a congressman could object to. 99 00:04:45,830 --> 00:04:49,430 Basically, it sounded cool. 100 00:04:49,430 --> 00:04:52,540 So that's the origin of the name dynamic programming. 101 00:04:52,540 --> 00:04:53,630 So why is the called that? 102 00:04:53,630 --> 00:04:55,450 Who knows. 103 00:04:55,450 --> 00:04:56,780 I mean, now you know. 104 00:04:56,780 --> 00:04:59,430 But it's not-- it's a weird term. 105 00:04:59,430 --> 00:05:00,720 Just take it for what it is. 106 00:05:04,150 --> 00:05:06,820 It may make some kind of sense, but-- 107 00:05:06,820 --> 00:05:07,500 All right. 108 00:05:07,500 --> 00:05:14,570 So we are going to start with this example of how 109 00:05:14,570 --> 00:05:16,800 to compute Fibonacci numbers. 110 00:05:16,800 --> 00:05:18,700 And maybe before we actually start 111 00:05:18,700 --> 00:05:23,250 I'm going to give you a sneak peak of what 112 00:05:23,250 --> 00:05:26,480 you can think of dynamic programming as. 113 00:05:26,480 --> 00:05:37,830 And this equation, so to speak, is 114 00:05:37,830 --> 00:05:42,400 going to change throughout today's lecture. 115 00:05:42,400 --> 00:05:43,970 In the end we'll settle on a sort 116 00:05:43,970 --> 00:05:46,760 of more accurate perspective. 117 00:05:46,760 --> 00:05:49,020 The basic idea of dynamic programming 118 00:05:49,020 --> 00:05:53,120 is to take a problem, split it into subproblems, 119 00:05:53,120 --> 00:05:55,780 solve those subproblems, and reuse the solutions 120 00:05:55,780 --> 00:05:56,720 to your subproblems. 121 00:05:56,720 --> 00:05:59,520 It's like a lesson in recycling. 122 00:05:59,520 --> 00:06:03,290 So we'll see that in Fibonacci numbers. 123 00:06:03,290 --> 00:06:06,270 So you remember Fibonacci numbers, right? 124 00:06:06,270 --> 00:06:10,445 The number of rabbits you have on day n, if they reproduce. 125 00:06:15,020 --> 00:06:18,630 We've mentioned them before, we're talking about AVL trees, 126 00:06:18,630 --> 00:06:19,910 I think. 127 00:06:19,910 --> 00:06:23,290 So this is the usual-- you can think 128 00:06:23,290 --> 00:06:26,710 of it as a recursive definition or recurrence on Fibonacci 129 00:06:26,710 --> 00:06:27,770 numbers. 130 00:06:27,770 --> 00:06:30,890 It's the definition of what the nth Fibonacci number is. 131 00:06:30,890 --> 00:06:34,810 So let's suppose our goal-- an algorithmic problem is, 132 00:06:34,810 --> 00:06:39,220 compute the nth Fibonacci number. 133 00:06:39,220 --> 00:06:42,710 And I'm going to assume here that that fits in a word. 134 00:06:42,710 --> 00:06:44,980 And so basic arithmetic, addition, 135 00:06:44,980 --> 00:06:48,100 whatever's constant time per operation. 136 00:06:48,100 --> 00:06:50,590 So how do we do it? 137 00:06:50,590 --> 00:06:52,200 You all know how to do it. 138 00:06:52,200 --> 00:06:56,610 Anyways-- but I'm going to give you the dynamic programming 139 00:06:56,610 --> 00:06:57,790 perspective on things. 140 00:06:57,790 --> 00:07:00,500 So this will seem kind of obvious, 141 00:07:00,500 --> 00:07:03,610 but it is-- we're going to apply exactly the same principles 142 00:07:03,610 --> 00:07:06,610 that we will apply over and over in dynamic programming. 143 00:07:06,610 --> 00:07:11,910 But here it's in a very familiar setting. 144 00:07:11,910 --> 00:07:16,430 So we're going to start with the naive recursive algorithm. 145 00:07:24,850 --> 00:07:29,150 And that is, if you want to compute the nth Fibonacci 146 00:07:29,150 --> 00:07:32,010 number, you check whether you're in the base case. 147 00:07:35,890 --> 00:07:38,520 I'm going to write it this way. 148 00:07:55,230 --> 00:07:57,320 So f is just our return value. 149 00:07:57,320 --> 00:07:59,470 You'll see why I write it this way in a moment. 150 00:07:59,470 --> 00:08:01,270 Then you return f. 151 00:08:01,270 --> 00:08:03,030 In the base case it's 1, otherwise 152 00:08:03,030 --> 00:08:05,860 you recursively call Fibonacci of n minus 1. 153 00:08:05,860 --> 00:08:08,520 You recursively call Fibonacci of n minus 2. 154 00:08:08,520 --> 00:08:10,600 Add them together, return that. 155 00:08:10,600 --> 00:08:12,762 This is a correct algorithm. 156 00:08:12,762 --> 00:08:15,160 Is it a good algorithm? 157 00:08:15,160 --> 00:08:16,255 No. 158 00:08:16,255 --> 00:08:17,230 It's very bad. 159 00:08:17,230 --> 00:08:18,260 Exponential time. 160 00:08:23,450 --> 00:08:25,480 How do we know it's exponential time, 161 00:08:25,480 --> 00:08:27,730 other than from experience? 162 00:08:27,730 --> 00:08:31,880 Well, we can write the running time as recurrence. 163 00:08:31,880 --> 00:08:34,990 T of n represents the time to compute the nth Fibonacci 164 00:08:34,990 --> 00:08:35,780 number. 165 00:08:35,780 --> 00:08:39,440 How can I write the recurrence? 166 00:08:39,440 --> 00:08:41,630 You're gonna throwback to the early lectures, divide 167 00:08:41,630 --> 00:08:42,606 and conquer. 168 00:08:48,950 --> 00:08:50,890 I hear whispers. 169 00:08:50,890 --> 00:08:51,390 Yeah? 170 00:08:51,390 --> 00:08:52,366 AUDIENCE: [INAUDIBLE]. 171 00:08:55,320 --> 00:08:57,081 PROFESSOR: Yeah. 172 00:08:57,081 --> 00:09:03,340 T of n minus 1 plus t of n minus 2 plus constant. 173 00:09:07,190 --> 00:09:08,865 I don't know how many you have by now. 174 00:09:11,510 --> 00:09:13,540 So to create the nth Fibonacci number 175 00:09:13,540 --> 00:09:15,790 we have to compute the n minus first Fibonacci number, 176 00:09:15,790 --> 00:09:17,456 and the n minus second Fibonacci number. 177 00:09:17,456 --> 00:09:19,890 That's these two recursions. 178 00:09:19,890 --> 00:09:21,760 And then we take constant time otherwise. 179 00:09:21,760 --> 00:09:24,910 We do constant number of additions, comparisons. 180 00:09:24,910 --> 00:09:27,920 Return all these operations-- take constant time. 181 00:09:27,920 --> 00:09:29,550 So that's a recurrence. 182 00:09:29,550 --> 00:09:31,390 How do we solve this recurrence? 183 00:09:31,390 --> 00:09:35,720 Well one way is to see this is the Fibonacci recurrence. 184 00:09:35,720 --> 00:09:37,350 So it's the same thing. 185 00:09:37,350 --> 00:09:38,970 There's this plus whatever. 186 00:09:38,970 --> 00:09:41,720 But in particular, this is at least the nth Fibonacci number. 187 00:09:41,720 --> 00:09:43,350 And if you know Fibonacci stuff, that's 188 00:09:43,350 --> 00:09:48,440 about the golden ratio to the nth power. 189 00:09:48,440 --> 00:09:50,650 Which is bad. 190 00:09:50,650 --> 00:09:52,930 We had a similar recurrence in AVL trees. 191 00:09:52,930 --> 00:09:55,850 And so another way to solve it-- it's 192 00:09:55,850 --> 00:09:59,100 just good review-- say, oh well, that's 193 00:09:59,100 --> 00:10:03,600 at least 2 times t of n minus 2. 194 00:10:03,600 --> 00:10:05,020 Because it's going to be monotone. 195 00:10:05,020 --> 00:10:07,331 The bigger n is, the more work you have to do. 196 00:10:07,331 --> 00:10:08,830 Because to do the nth thing you have 197 00:10:08,830 --> 00:10:10,770 to do the n minus first thing. 198 00:10:10,770 --> 00:10:14,260 So we could just reduce t of n minus 1 to t of n minus 2. 199 00:10:14,260 --> 00:10:16,620 That will give us a lower bound. 200 00:10:16,620 --> 00:10:19,700 And now these two terms-- now this is sort of an easy thing. 201 00:10:19,700 --> 00:10:22,950 You see that you're multiplying by 2 each time. 202 00:10:22,950 --> 00:10:24,670 You're subtracting 2 from n each time. 203 00:10:24,670 --> 00:10:27,270 How many times can I subtract 2 from n? 204 00:10:27,270 --> 00:10:30,390 N/2 times, before I get down to a constant. 205 00:10:30,390 --> 00:10:37,740 And so this is equal to 2 to the n over 2-- 206 00:10:37,740 --> 00:10:39,460 I mean, times some constant, which 207 00:10:39,460 --> 00:10:41,900 is what you get in the base case. 208 00:10:41,900 --> 00:10:45,970 So I guess I should say theta. 209 00:10:45,970 --> 00:10:48,890 This thing is theta that. 210 00:10:48,890 --> 00:10:49,390 OK. 211 00:10:49,390 --> 00:10:51,320 So it's at least that big. 212 00:10:51,320 --> 00:10:56,302 And the right constant is phi. 213 00:10:56,302 --> 00:10:58,620 And the base of the exponent. 214 00:10:58,620 --> 00:10:59,120 OK. 215 00:10:59,120 --> 00:11:00,203 So that's a bad algorithm. 216 00:11:00,203 --> 00:11:03,690 We all know it's a bad algorithm. 217 00:11:03,690 --> 00:11:06,670 But I'm going to give you a general approach for making 218 00:11:06,670 --> 00:11:09,000 bad algorithms like this good. 219 00:11:09,000 --> 00:11:11,036 And that general approach is called memoization. 220 00:11:14,170 --> 00:11:16,600 We'll go over here. 221 00:11:19,910 --> 00:11:21,900 And this is a technique of dynamic programming. 222 00:11:24,708 --> 00:11:28,370 So I'm going to call this the memoized dynamic programming 223 00:11:28,370 --> 00:11:28,870 algorithm. 224 00:11:36,840 --> 00:11:44,270 So did I settle on using memo in the notes? 225 00:11:44,270 --> 00:11:44,770 Yeah. 226 00:11:48,570 --> 00:11:50,520 The idea is simple. 227 00:11:50,520 --> 00:11:52,870 Whenever we compute a Fibonacci number 228 00:11:52,870 --> 00:11:55,167 we put it in a dictionary. 229 00:11:55,167 --> 00:11:57,250 And then when we need to compute the nth Fibonacci 230 00:11:57,250 --> 00:11:58,825 number we check, is it already in the dictionary? 231 00:11:58,825 --> 00:12:00,241 Did we already solve this problem? 232 00:12:00,241 --> 00:12:03,020 If so, return that answer. 233 00:12:03,020 --> 00:12:05,085 Otherwise, computer it. 234 00:12:05,085 --> 00:12:06,960 You'll see the transformation is very simple. 235 00:12:57,810 --> 00:12:59,040 OK. 236 00:12:59,040 --> 00:13:04,650 These two lines are identical to these two lines. 237 00:13:04,650 --> 00:13:06,320 So you can see how the transformation 238 00:13:06,320 --> 00:13:07,730 works in general. 239 00:13:07,730 --> 00:13:10,856 You could do this with any recursive algorithm. 240 00:13:10,856 --> 00:13:14,700 The memoization transformation on that algorithm-- 241 00:13:14,700 --> 00:13:19,050 which is, we initially make an empty dictionary called memo. 242 00:13:19,050 --> 00:13:22,950 And before we actually do the computation we say, 243 00:13:22,950 --> 00:13:26,820 well, check whether this version of the Fibonacci problem, 244 00:13:26,820 --> 00:13:31,432 computing f of n, is already in our dictionary. 245 00:13:31,432 --> 00:13:33,265 So if that key is already in the dictionary, 246 00:13:33,265 --> 00:13:35,935 we return the corresponding value in the dictionary. 247 00:13:38,960 --> 00:13:41,590 And then once we've computed the nth Fibonacci number, 248 00:13:41,590 --> 00:13:44,780 if we bothered to do this, if this didn't apply, 249 00:13:44,780 --> 00:13:47,140 then we store it in the memo table. 250 00:13:47,140 --> 00:13:50,900 So we say well, if you ever need to compute f of n again, 251 00:13:50,900 --> 00:13:52,370 here it is. 252 00:13:52,370 --> 00:13:54,820 And then we return that value. 253 00:13:54,820 --> 00:13:57,192 So this is a general procedure. 254 00:13:57,192 --> 00:14:00,910 It can apply to any recursive algorithm 255 00:14:00,910 --> 00:14:04,900 with no side effects I guess, technically. 256 00:14:04,900 --> 00:14:07,789 And it turns out, this makes the algorithm efficient. 257 00:14:07,789 --> 00:14:09,955 Now there's a lot of ways to see why it's efficient. 258 00:14:12,680 --> 00:14:15,260 In general, maybe it's helpful to think about the recursion 259 00:14:15,260 --> 00:14:16,220 tree. 260 00:14:16,220 --> 00:14:19,660 So if you want to compute fn in the old algorithm, 261 00:14:19,660 --> 00:14:22,550 we compute fn minus 1 and fn minus two 262 00:14:22,550 --> 00:14:23,940 completely separately. 263 00:14:23,940 --> 00:14:29,820 To compute fn minus 1 we compute fn minus 2 and fn minus 3. 264 00:14:29,820 --> 00:14:34,720 To compute fn minus 2 we compute fn minus 3 and fn minus 4. 265 00:14:34,720 --> 00:14:35,620 And so on. 266 00:14:35,620 --> 00:14:39,490 And you can see why that's exponential in n. 267 00:14:39,490 --> 00:14:44,250 Because we're only decrementing n by one or two each time. 268 00:14:44,250 --> 00:14:48,200 But then you observe, hey, these fn minus 3's are the same. 269 00:14:48,200 --> 00:14:50,935 I should really only have to compute them once. 270 00:14:50,935 --> 00:14:52,310 And that's what we're doing here. 271 00:14:52,310 --> 00:14:55,660 The first time you call fn minus 3, you do work. 272 00:14:55,660 --> 00:14:58,780 But once it's done and you go over to this other recursive 273 00:14:58,780 --> 00:15:00,760 call, this will just get cut off. 274 00:15:00,760 --> 00:15:02,040 There's no tree here. 275 00:15:02,040 --> 00:15:04,180 Here we might have some recursive calling. 276 00:15:04,180 --> 00:15:08,700 Here we won't, because it's already in the memo table. 277 00:15:08,700 --> 00:15:11,930 In fact, this already happens with fn minus 2. 278 00:15:11,930 --> 00:15:15,600 This whole tree disappears because fn minus 2 279 00:15:15,600 --> 00:15:17,610 has already been done. 280 00:15:17,610 --> 00:15:18,110 OK. 281 00:15:18,110 --> 00:15:21,090 So it's clear why it improves things. 282 00:15:21,090 --> 00:15:24,750 So in fact you can argue that this call will be free 283 00:15:24,750 --> 00:15:27,540 because you already did the work in here. 284 00:15:27,540 --> 00:15:30,700 But I want to give you a very particular way of thinking 285 00:15:30,700 --> 00:15:40,015 about why this is efficient, which is following. 286 00:16:06,550 --> 00:16:09,800 So you could write down a recurrence for the running time 287 00:16:09,800 --> 00:16:10,300 here. 288 00:16:10,300 --> 00:16:13,050 But in some sense recurrences aren't quite the right way 289 00:16:13,050 --> 00:16:15,240 of thinking about this because recursion 290 00:16:15,240 --> 00:16:17,220 is kind of a rare thing. 291 00:16:17,220 --> 00:16:20,840 If you're calling Fibonacci of some value, k, 292 00:16:20,840 --> 00:16:23,700 you're only going to make recursive calls the first time 293 00:16:23,700 --> 00:16:24,930 you call Fibonacci of k. 294 00:16:24,930 --> 00:16:28,250 Because henceforth, you've put it 295 00:16:28,250 --> 00:16:30,860 in the memo table you will not recurse. 296 00:16:30,860 --> 00:16:35,370 So you can think of there being two versions 297 00:16:35,370 --> 00:16:36,640 of calling Fibonacci of k. 298 00:16:36,640 --> 00:16:40,950 There's the first time, which is the non-memoized version that 299 00:16:40,950 --> 00:16:42,960 does recursion-- does some work. 300 00:16:42,960 --> 00:16:46,090 And then every time henceforth you're 301 00:16:46,090 --> 00:16:48,080 doing memoized calls of Fibonacci of k, 302 00:16:48,080 --> 00:16:51,620 and those cost constant time. 303 00:16:51,620 --> 00:16:59,820 So the memoized calls cost constant time. 304 00:16:59,820 --> 00:17:03,120 So we can think of them as basically free. 305 00:17:03,120 --> 00:17:06,210 That's when you call Fibonacci of n minus 2, 306 00:17:06,210 --> 00:17:09,210 because that's a memoized call, you really 307 00:17:09,210 --> 00:17:10,990 don't pay anything for it. 308 00:17:10,990 --> 00:17:12,781 I mean, you're already paying constant time 309 00:17:12,781 --> 00:17:14,250 to do addition and whatever. 310 00:17:14,250 --> 00:17:16,089 So you don't have to worry about the time. 311 00:17:16,089 --> 00:17:18,900 There's no recursion here. 312 00:17:18,900 --> 00:17:21,670 And then what we care about is that the number 313 00:17:21,670 --> 00:17:26,950 of non-memorized calls, which is the first time you 314 00:17:26,950 --> 00:17:33,925 call Fibonacci of k, is n. 315 00:17:33,925 --> 00:17:35,640 No theta is even necessary. 316 00:17:35,640 --> 00:17:38,990 We are going to call Fibonacci of 1. 317 00:17:38,990 --> 00:17:41,860 At some point we're going to call Fibonacci of 2 318 00:17:41,860 --> 00:17:46,039 at some point, and the original call is Fibonacci of n. 319 00:17:46,039 --> 00:17:48,080 All of those things will be called at some point. 320 00:17:48,080 --> 00:17:49,650 That's pretty easy to see. 321 00:17:49,650 --> 00:17:52,050 But in particular, certainly at most this, 322 00:17:52,050 --> 00:17:53,900 we never call Fibonacci of n plus 1 323 00:17:53,900 --> 00:17:55,820 to compute Fibonacci of n. 324 00:17:55,820 --> 00:17:57,460 So it's at most n calls. 325 00:17:57,460 --> 00:18:00,880 Indeed it will be exactly n calls that are not memoized. 326 00:18:00,880 --> 00:18:02,130 Those ones we have to pay for. 327 00:18:02,130 --> 00:18:03,650 How much do we have to pay? 328 00:18:03,650 --> 00:18:07,610 Well, if you don't count the recursion-- which 329 00:18:07,610 --> 00:18:09,840 is what this recurrence does-- if you ignore 330 00:18:09,840 --> 00:18:13,235 recursion then the total amount of work done here is constant. 331 00:18:16,110 --> 00:18:30,630 So I will say the non-recursive work per call is constant. 332 00:18:30,630 --> 00:18:34,010 And therefore I claim that the running time is 333 00:18:34,010 --> 00:18:37,870 constant-- I'm sorry, is linear. 334 00:18:37,870 --> 00:18:41,390 Constant would be pretty amazing. 335 00:18:41,390 --> 00:18:44,310 This is actually not the best algorithm-- as an aside. 336 00:18:44,310 --> 00:18:46,720 The best algorithm for computing the nth Fibonacci number 337 00:18:46,720 --> 00:18:50,299 uses log n arithmetic operations. 338 00:18:50,299 --> 00:18:51,840 So you can do better, but if you want 339 00:18:51,840 --> 00:18:55,430 to see that you should take 6046. 340 00:18:55,430 --> 00:18:55,950 OK. 341 00:18:55,950 --> 00:18:57,866 We're just going to get to linear today, which 342 00:18:57,866 --> 00:19:01,430 is a lot better than exponential. 343 00:19:01,430 --> 00:19:03,890 So why linear? 344 00:19:03,890 --> 00:19:07,700 Because there's n non-memoize calls, and each of them 345 00:19:07,700 --> 00:19:08,690 cost constant. 346 00:19:08,690 --> 00:19:12,330 So it's the product of those two numbers. 347 00:19:12,330 --> 00:19:14,820 This is an important idea. 348 00:19:14,820 --> 00:19:18,320 And it's so important I'm going to write it 349 00:19:18,320 --> 00:19:25,080 down again in a slightly more general framework. 350 00:19:25,080 --> 00:19:37,400 In general, in dynamic programming-- 351 00:19:37,400 --> 00:19:40,540 I didn't say why it's called memoization. 352 00:19:40,540 --> 00:19:42,363 The idea is you have this memo pad where 353 00:19:42,363 --> 00:19:44,810 you write down all your scratch work. 354 00:19:44,810 --> 00:19:46,495 That's this memo dictionary. 355 00:19:46,495 --> 00:19:49,390 And to memoize is to write down on your memo pad. 356 00:19:49,390 --> 00:19:50,530 I didn't make it up. 357 00:19:50,530 --> 00:19:52,630 Another crazy term. 358 00:19:52,630 --> 00:19:55,450 It means remember. 359 00:19:55,450 --> 00:19:59,610 And then you remember all the solutions that you've done. 360 00:19:59,610 --> 00:20:02,380 And then you reuse those solutions. 361 00:20:02,380 --> 00:20:04,460 Now these solutions are not really a solution 362 00:20:04,460 --> 00:20:06,470 to the problem that I care about. 363 00:20:06,470 --> 00:20:08,970 The problem I care about is computing the nth Fibonacci 364 00:20:08,970 --> 00:20:09,850 number. 365 00:20:09,850 --> 00:20:13,230 To get there I had to compute other Fibonacci numbers. 366 00:20:13,230 --> 00:20:15,060 Why? 367 00:20:15,060 --> 00:20:16,730 Because I had a recursive formulation. 368 00:20:16,730 --> 00:20:19,220 This is not always the way to solve a problem. 369 00:20:19,220 --> 00:20:22,550 But usually when you're solving something 370 00:20:22,550 --> 00:20:28,030 you can split it into parts, into subproblems, we call them. 371 00:20:28,030 --> 00:20:29,700 They're not always of the same flavor 372 00:20:29,700 --> 00:20:31,450 as your original goal problem, but there's 373 00:20:31,450 --> 00:20:34,290 some kind of related parts. 374 00:20:34,290 --> 00:20:38,040 And this is the big challenge in designing a dynamic program, 375 00:20:38,040 --> 00:20:41,410 is to figure out what are the subproblems. 376 00:20:41,410 --> 00:20:43,080 Let's say, the first thing I want 377 00:20:43,080 --> 00:20:45,621 to know about a dynamic program, is what are the subproblems. 378 00:20:47,610 --> 00:20:51,640 Somehow they are designed to help solve your actual problem. 379 00:20:58,650 --> 00:21:01,650 And the idea of memoization is, once you solve a subproblem, 380 00:21:01,650 --> 00:21:02,650 write down the answer. 381 00:21:02,650 --> 00:21:05,350 If you ever need to solve that same problem again 382 00:21:05,350 --> 00:21:07,400 you reuse the answer. 383 00:21:07,400 --> 00:21:09,134 So that is the core idea. 384 00:21:09,134 --> 00:21:10,800 And so in this sense dynamic programming 385 00:21:10,800 --> 00:21:16,025 is essentially recursion plus memoization. 386 00:21:23,490 --> 00:21:27,450 And so in this case these are the subproblems. 387 00:21:27,450 --> 00:21:29,950 Fibonacci of 1 through Fibonacci of n. 388 00:21:29,950 --> 00:21:31,677 The one we care about is Fibonacci of n. 389 00:21:31,677 --> 00:21:33,760 But to get there we solve these other subproblems. 390 00:21:36,290 --> 00:21:40,180 In all cases, if this is the situation-- so 391 00:21:40,180 --> 00:21:43,280 for any dynamic program, the running time 392 00:21:43,280 --> 00:21:46,560 is going to be equal to the number of different subproblems 393 00:21:46,560 --> 00:21:50,590 you might have to solve, or that you do solve, 394 00:21:50,590 --> 00:21:54,480 times the amount of time you spend per subproblem. 395 00:22:00,950 --> 00:22:01,450 OK. 396 00:22:01,450 --> 00:22:06,960 In this situation we had n subproblems. 397 00:22:06,960 --> 00:22:10,900 And for each of them we spent constant time. 398 00:22:10,900 --> 00:22:13,580 And when I measure the time per subproblem 399 00:22:13,580 --> 00:22:15,890 which, in the Fibonacci case I claim is constant, 400 00:22:15,890 --> 00:22:18,790 I ignore recursive calls. 401 00:22:18,790 --> 00:22:19,914 That's the key. 402 00:22:19,914 --> 00:22:21,330 We don't have to solve recurrences 403 00:22:21,330 --> 00:22:22,630 with dynamic programming. 404 00:22:22,630 --> 00:22:23,355 Yay. 405 00:22:23,355 --> 00:22:25,250 No recurrences necessary. 406 00:22:25,250 --> 00:22:25,750 OK. 407 00:22:25,750 --> 00:22:29,840 Don't count recursions. 408 00:22:34,280 --> 00:22:36,800 Obviously, don't count memoized recursions. 409 00:22:36,800 --> 00:22:39,750 The reason is, I only need to count them once. 410 00:22:39,750 --> 00:22:42,540 After the first time I do it, it's free. 411 00:22:42,540 --> 00:22:45,835 So I count how many different subproblems do I need to do? 412 00:22:45,835 --> 00:22:48,210 These are they going to be the expensive recursions where 413 00:22:48,210 --> 00:22:49,810 I do work, I do some amount of work, 414 00:22:49,810 --> 00:22:52,060 but I don't count the recursions because otherwise I'd 415 00:22:52,060 --> 00:22:53,600 be double counting. 416 00:22:53,600 --> 00:22:56,930 I only want to count each subproblem once, 417 00:22:56,930 --> 00:22:59,010 and then this will solve it. 418 00:22:59,010 --> 00:23:00,840 So a simple idea. 419 00:23:00,840 --> 00:23:03,860 In general, dynamic programming is a super simple idea. 420 00:23:03,860 --> 00:23:05,390 It's nothing fancy. 421 00:23:05,390 --> 00:23:07,440 It's basically just memoization. 422 00:23:07,440 --> 00:23:10,730 There is one extra trick we're going to pull out, 423 00:23:10,730 --> 00:23:12,770 but that's the idea. 424 00:23:12,770 --> 00:23:14,110 All right. 425 00:23:14,110 --> 00:23:24,010 Let me tell you another perspective. 426 00:23:24,010 --> 00:23:28,050 This is the one maybe most commonly taught. 427 00:23:28,050 --> 00:23:31,260 Is to think of-- but I'm not a particular fan of it. 428 00:23:31,260 --> 00:23:32,470 I really like memoization. 429 00:23:32,470 --> 00:23:35,240 I think it's a simple idea. 430 00:23:35,240 --> 00:23:38,500 And as long as you remember this formula here, 431 00:23:38,500 --> 00:23:39,900 it's really easy to work with. 432 00:23:43,240 --> 00:23:45,560 But some people like to think of it this way. 433 00:23:45,560 --> 00:23:49,315 And so you can pick whichever way you find most intuitive. 434 00:23:49,315 --> 00:23:51,440 Instead of thinking of a recursive algorithm, which 435 00:23:51,440 --> 00:23:55,940 in some sense starts at the top of what you want to solve 436 00:23:55,940 --> 00:23:58,660 and works its way down, you could do the reverse. 437 00:23:58,660 --> 00:24:00,829 You could start at the bottom and work your way up. 438 00:24:00,829 --> 00:24:02,370 And this is probably how you normally 439 00:24:02,370 --> 00:24:03,994 think about computing Fibonacci numbers 440 00:24:03,994 --> 00:24:05,754 or how you learned it before. 441 00:24:05,754 --> 00:24:07,670 I'm going to write it in a slightly funny way. 442 00:24:31,655 --> 00:24:33,780 The point I want to make is that the transformation 443 00:24:33,780 --> 00:24:38,840 I'm doing from the naive recursive algorithm, 444 00:24:38,840 --> 00:24:42,080 to the memoized algorithm, to the bottom-up algorithm 445 00:24:42,080 --> 00:24:44,030 is completely automated. 446 00:24:44,030 --> 00:24:46,650 I'm not thinking, I'm just doing. 447 00:24:46,650 --> 00:24:47,470 OK. 448 00:24:47,470 --> 00:24:49,500 It's easy. 449 00:24:49,500 --> 00:24:52,470 This code is exactly the same as this code 450 00:24:52,470 --> 00:24:56,590 and as that code, except I replaced n by k. 451 00:24:56,590 --> 00:24:59,850 Just because I needed a couple of different n values here. 452 00:24:59,850 --> 00:25:01,380 Or I want to iterate over n values. 453 00:25:04,110 --> 00:25:06,490 And then there's this stuff around that code 454 00:25:06,490 --> 00:25:07,690 which is just formulaic. 455 00:25:11,000 --> 00:25:13,460 A little bit of thought goes into this for loop, 456 00:25:13,460 --> 00:25:15,160 but that's it. 457 00:25:15,160 --> 00:25:15,660 OK. 458 00:25:15,660 --> 00:25:19,110 This does exactly the same thing as the memoized algorithm. 459 00:25:22,525 --> 00:25:24,150 Maybe it takes a little bit of thinking 460 00:25:24,150 --> 00:25:26,840 to realize, if you unroll all the recursion that's happening 461 00:25:26,840 --> 00:25:29,350 here and just write it out sequentially, 462 00:25:29,350 --> 00:25:31,830 this is exactly what's happening. 463 00:25:31,830 --> 00:25:34,580 This code does exactly the same additions, exactly 464 00:25:34,580 --> 00:25:37,020 the same computations as this. 465 00:25:37,020 --> 00:25:39,690 The only difference is how you get there. 466 00:25:39,690 --> 00:25:42,310 Here we're using a loop, here we're using recursion. 467 00:25:42,310 --> 00:25:45,559 But the same things happen in the same order. 468 00:25:45,559 --> 00:25:47,350 It's really no difference between the code. 469 00:25:47,350 --> 00:25:49,683 This code's probably going to be more efficient practice 470 00:25:49,683 --> 00:25:52,980 because you don't make function calls so much. 471 00:25:52,980 --> 00:25:55,840 In fact I made a little mistake here. 472 00:25:55,840 --> 00:25:57,240 This is not a function call, it's 473 00:25:57,240 --> 00:25:59,470 just a lookup into a table. 474 00:25:59,470 --> 00:26:01,180 Here I'm using a hash table to be simple, 475 00:26:01,180 --> 00:26:02,721 but of course you could use an array. 476 00:26:05,920 --> 00:26:10,330 But they're both constant time with good hashing. 477 00:26:10,330 --> 00:26:10,830 All right. 478 00:26:10,830 --> 00:26:12,380 So is it clear what this is doing? 479 00:26:12,380 --> 00:26:13,580 I think so. 480 00:26:13,580 --> 00:26:16,410 I think I made a little typo. 481 00:26:16,410 --> 00:26:21,630 So we have to compute-- oh, another typo. 482 00:26:21,630 --> 00:26:27,010 We have to compute f1 up to fn, which in python is that. 483 00:26:27,010 --> 00:26:31,860 And we compute it exactly how we used to. 484 00:26:31,860 --> 00:26:34,020 Except now, instead of recursing, 485 00:26:34,020 --> 00:26:38,170 I know that when I'm computing the k Fibonacci number-- man. 486 00:26:38,170 --> 00:26:39,135 So many typos. 487 00:26:39,135 --> 00:26:40,504 AUDIENCE: [LAUGHTER] 488 00:26:40,504 --> 00:26:42,920 PROFESSOR: You guys are laughing. 489 00:26:42,920 --> 00:26:45,230 When I compute the kth Fibonacci number 490 00:26:45,230 --> 00:26:48,230 I know that I've already computed the previous two. 491 00:26:48,230 --> 00:26:48,730 Why? 492 00:26:48,730 --> 00:26:50,550 Because I'm doing them in increasing order. 493 00:26:50,550 --> 00:26:52,620 Nothing fancy. 494 00:26:52,620 --> 00:26:55,230 Then I can just do this and the solutions 495 00:26:55,230 --> 00:26:56,750 will just be waiting there. 496 00:26:56,750 --> 00:26:58,450 If they work, I'd get a key error. 497 00:26:58,450 --> 00:27:00,120 So I'd know that there's a bug. 498 00:27:00,120 --> 00:27:01,670 But in fact, I won't get a key error. 499 00:27:01,670 --> 00:27:05,090 I will have always computed these things already. 500 00:27:05,090 --> 00:27:06,610 Then I store it in my table. 501 00:27:06,610 --> 00:27:07,290 Then I iterate. 502 00:27:07,290 --> 00:27:11,250 Eventually I've solved all the subproblems, f1 through fn. 503 00:27:11,250 --> 00:27:14,140 And the one I cared about was the nth one. 504 00:27:14,140 --> 00:27:14,640 OK. 505 00:27:14,640 --> 00:27:17,490 So straightforward. 506 00:27:17,490 --> 00:27:19,432 I do this because I don't really want 507 00:27:19,432 --> 00:27:21,140 to have to go through this transformation 508 00:27:21,140 --> 00:27:22,870 for every single problem we do. 509 00:27:22,870 --> 00:27:25,150 I'm doing it in Fibonacci because it's super easy 510 00:27:25,150 --> 00:27:27,080 to write the code out explicitly. 511 00:27:27,080 --> 00:27:29,200 But you can do it for all of the dynamic programs 512 00:27:29,200 --> 00:27:33,130 that we cover in the next four lectures. 513 00:27:33,130 --> 00:27:33,630 OK. 514 00:27:33,630 --> 00:27:35,810 I'm going to give you now the general case. 515 00:27:35,810 --> 00:27:39,260 This was the special Fibonacci version. 516 00:27:39,260 --> 00:27:44,230 In general, the bottom-up does exactly the same computation 517 00:27:44,230 --> 00:27:45,565 as the memoized version. 518 00:27:51,270 --> 00:27:59,920 And what we're doing is actually a topological sort 519 00:27:59,920 --> 00:28:05,195 of the subproblem dependency DAG. 520 00:28:13,840 --> 00:28:17,240 So in this case, the dependency DAG is very simple. 521 00:28:17,240 --> 00:28:21,330 In order to compute-- I'll do it backwards. 522 00:28:21,330 --> 00:28:26,450 In order to compute fn, I need to know fn minus 1 523 00:28:26,450 --> 00:28:28,820 and fn minus 2. 524 00:28:28,820 --> 00:28:32,090 If I know those I can compute fn. 525 00:28:32,090 --> 00:28:35,080 Then there's fn minus 3, which is 526 00:28:35,080 --> 00:28:38,450 necessary to compute this one, and that one, and so on. 527 00:28:38,450 --> 00:28:40,470 So you see what this DAG looks like. 528 00:28:40,470 --> 00:28:42,160 Now, I've drawn it conveniently so 529 00:28:42,160 --> 00:28:43,510 all the edges go left to right. 530 00:28:43,510 --> 00:28:46,420 So this is a topological order from left to right. 531 00:28:46,420 --> 00:28:50,760 And so I just need to do f1, f2, up to fn in order. 532 00:28:50,760 --> 00:28:53,850 Usually it's totally obvious what order 533 00:28:53,850 --> 00:28:55,560 to solve the subproblems in. 534 00:28:55,560 --> 00:28:58,320 But in general, what you should have in mind 535 00:28:58,320 --> 00:29:00,430 is that we are doing a topological sort. 536 00:29:00,430 --> 00:29:03,070 Here we just did it in our heads because it's so easy. 537 00:29:03,070 --> 00:29:04,220 And usually it's so easy. 538 00:29:04,220 --> 00:29:05,480 It's just a for loop. 539 00:29:05,480 --> 00:29:06,430 Nothing fancy. 540 00:29:09,900 --> 00:29:10,500 All right. 541 00:29:13,404 --> 00:29:14,450 I'm missing an arrow. 542 00:29:20,030 --> 00:29:22,554 All right. 543 00:29:22,554 --> 00:29:24,845 Let's do something a little more interesting, shall we? 544 00:29:37,971 --> 00:29:38,470 All right. 545 00:29:38,470 --> 00:29:41,770 One thing you can do from this bottom-up perspective 546 00:29:41,770 --> 00:29:42,775 is you can save space. 547 00:29:46,310 --> 00:29:48,572 Storage space in the algorithm. 548 00:29:48,572 --> 00:29:51,110 We don't usually worry about space in this class, 549 00:29:51,110 --> 00:29:55,910 but it matters in reality. 550 00:29:55,910 --> 00:29:57,500 So here we're building a table size, 551 00:29:57,500 --> 00:29:59,830 n, but in fact we really only need 552 00:29:59,830 --> 00:30:02,280 to remember the last two values. 553 00:30:02,280 --> 00:30:04,190 So you could just store the last two values, 554 00:30:04,190 --> 00:30:07,620 and each time you make a new one delete the oldest. 555 00:30:07,620 --> 00:30:10,100 so by thinking a little bit here you 556 00:30:10,100 --> 00:30:12,470 realize you only need constant space. 557 00:30:12,470 --> 00:30:15,790 Still linear time, but constant space. 558 00:30:15,790 --> 00:30:17,444 And that's often the case. 559 00:30:17,444 --> 00:30:18,860 From the bottom-up perspective you 560 00:30:18,860 --> 00:30:20,699 see what you really need to store, 561 00:30:20,699 --> 00:30:21,990 what you need to keep track of. 562 00:30:24,830 --> 00:30:26,359 All right. 563 00:30:26,359 --> 00:30:28,400 I guess another nice thing about this perspective 564 00:30:28,400 --> 00:30:30,710 is, the running time is totally obvious. 565 00:30:30,710 --> 00:30:32,430 This is clearly constant time. 566 00:30:32,430 --> 00:30:34,730 So this is clearly linear time. 567 00:30:34,730 --> 00:30:37,030 Whereas, in this memoized algorithm 568 00:30:37,030 --> 00:30:40,110 you have to think about, when's it 569 00:30:40,110 --> 00:30:42,150 going to be memoized, when is it not? 570 00:30:42,150 --> 00:30:44,979 I still like this perspective because, with this rule, 571 00:30:44,979 --> 00:30:46,520 just multiply a number of subproblems 572 00:30:46,520 --> 00:30:49,110 by time per subproblem, you get the answer. 573 00:30:49,110 --> 00:30:54,300 But it's a little less obvious than code like this. 574 00:30:54,300 --> 00:30:57,060 So choose however you like to think about it. 575 00:31:00,340 --> 00:31:00,840 All right. 576 00:31:00,840 --> 00:31:02,700 We move onto shortest paths. 577 00:31:28,890 --> 00:31:32,310 So I'm again, as usual, thinking about single-source shortest 578 00:31:32,310 --> 00:31:33,980 paths. 579 00:31:33,980 --> 00:31:37,040 So we want to compute the shortest pathway from s 580 00:31:37,040 --> 00:31:41,550 to v for all v. OK. 581 00:31:41,550 --> 00:31:46,040 I'd like to write this initially as a naive recursive algorithm, 582 00:31:46,040 --> 00:31:49,900 which I can then memoize, which I can then bottom-upify. 583 00:31:49,900 --> 00:31:53,040 I just made that up. 584 00:31:53,040 --> 00:31:57,080 So how could I write this as a naive recursive algorithm? 585 00:31:57,080 --> 00:31:59,710 It's not so obvious. 586 00:31:59,710 --> 00:32:06,850 But first I'm going to tell you how, just as an oracle tells 587 00:32:06,850 --> 00:32:08,470 you, here's what you should do. 588 00:32:08,470 --> 00:32:10,950 But then we're going to think about-- go back, step back. 589 00:32:10,950 --> 00:32:11,992 Actually, it's up to you. 590 00:32:11,992 --> 00:32:13,491 I could tell you the answer and then 591 00:32:13,491 --> 00:32:15,070 we could figure out how we got there, 592 00:32:15,070 --> 00:32:18,381 or we could just figure out the answer. 593 00:32:18,381 --> 00:32:18,880 Preferences? 594 00:32:22,350 --> 00:32:22,934 Figure it out. 595 00:32:22,934 --> 00:32:23,433 All right. 596 00:32:23,433 --> 00:32:24,050 Good. 597 00:32:24,050 --> 00:32:26,140 No divine inspiration allowed. 598 00:32:26,140 --> 00:32:37,584 So let me give you a tool. 599 00:32:37,584 --> 00:32:38,760 The tool is guessing. 600 00:32:42,660 --> 00:32:45,880 This may sound silly, but it's a very powerful tool. 601 00:32:48,500 --> 00:32:51,320 The general idea is, suppose you don't know something 602 00:32:51,320 --> 00:32:53,360 but you'd like to know it. 603 00:32:53,360 --> 00:32:55,350 So what's the answer to this question? 604 00:32:55,350 --> 00:32:56,630 I don't know. 605 00:32:56,630 --> 00:32:57,860 Man, I really want a cushion. 606 00:32:57,860 --> 00:32:59,650 How am I going to answer the question? 607 00:32:59,650 --> 00:33:00,440 Guess. 608 00:33:00,440 --> 00:33:00,940 OK? 609 00:33:00,940 --> 00:33:02,530 AUDIENCE: [LAUGHTER] 610 00:33:02,530 --> 00:33:04,310 PROFESSOR: It's a tried and tested method 611 00:33:04,310 --> 00:33:05,490 for solving any problem. 612 00:33:16,920 --> 00:33:19,720 I'm kind of belaboring the point here. 613 00:33:19,720 --> 00:33:23,610 The algorithmic concept is, don't just try any guess. 614 00:33:23,610 --> 00:33:25,960 Try them all. 615 00:33:25,960 --> 00:33:26,747 OK? 616 00:33:26,747 --> 00:33:29,490 AUDIENCE: [LAUGHTER] 617 00:33:29,490 --> 00:33:30,810 PROFESSOR: Also pretty simple. 618 00:33:30,810 --> 00:33:32,881 I said dynamic programming was simple. 619 00:33:32,881 --> 00:33:33,380 OK. 620 00:33:38,400 --> 00:33:43,030 Try all guesses. 621 00:33:43,030 --> 00:33:45,430 This is central to the dynamic programming. 622 00:33:45,430 --> 00:33:52,470 I know it sounds obvious, but if I want to fix my equation here, 623 00:33:52,470 --> 00:33:58,260 dynamic programming is roughly recursion plus memoization. 624 00:33:58,260 --> 00:34:01,800 This should really be, plus guessing. 625 00:34:01,800 --> 00:34:06,340 Memoization, which is obvious, guessing which is obvious, 626 00:34:06,340 --> 00:34:08,928 are the central concepts to dynamic programming. 627 00:34:08,928 --> 00:34:11,219 I'm trying to make it sound easy because usually people 628 00:34:11,219 --> 00:34:12,802 have trouble with dynamic programming. 629 00:34:12,802 --> 00:34:15,310 It is easy. 630 00:34:15,310 --> 00:34:16,792 Try all the guesses. 631 00:34:16,792 --> 00:34:18,500 That's something a computer can do great. 632 00:34:18,500 --> 00:34:20,111 This is the brute force part. 633 00:34:20,111 --> 00:34:20,610 OK. 634 00:34:20,610 --> 00:34:23,690 But we're going to do it carefully. 635 00:34:23,690 --> 00:34:24,689 Not that carefully. 636 00:34:24,689 --> 00:34:26,690 I mean, we're just trying all the guesses. 637 00:34:26,690 --> 00:34:27,590 Take the best one. 638 00:34:33,960 --> 00:34:35,980 That's kind of important that we can choose one 639 00:34:35,980 --> 00:34:36,864 to be called best. 640 00:34:36,864 --> 00:34:38,239 That's why dynamic programming is 641 00:34:38,239 --> 00:34:39,610 good for optimization problems. 642 00:34:39,610 --> 00:34:42,110 You want to maximize something, minimize something, 643 00:34:42,110 --> 00:34:45,130 you try them all and then you can forget about all of them 644 00:34:45,130 --> 00:34:47,020 and just reduce it down to one thing which 645 00:34:47,020 --> 00:34:50,890 is the best one, or a best one. 646 00:34:50,890 --> 00:34:51,389 OK. 647 00:34:51,389 --> 00:34:53,830 So now I want you to try to apply 648 00:34:53,830 --> 00:34:56,639 this principle to shortest paths. 649 00:34:56,639 --> 00:34:59,380 Now I'm going to draw a picture which may help. 650 00:34:59,380 --> 00:35:08,790 We have the source, s, we have some vertex, 651 00:35:08,790 --> 00:35:11,370 v. We'd like to find the shortest-- 652 00:35:11,370 --> 00:35:13,870 a shortest path from s to v. 653 00:35:13,870 --> 00:35:16,250 Suppose I want to know what this shortest path is. 654 00:35:16,250 --> 00:35:18,092 Suppose this was it. 655 00:35:18,092 --> 00:35:19,790 You have an idea already? 656 00:35:19,790 --> 00:35:21,245 Yeah. 657 00:35:21,245 --> 00:35:27,550 AUDIENCE: What you could do is you could look at everywhere 658 00:35:27,550 --> 00:35:29,490 you can go from s. 659 00:35:29,490 --> 00:35:33,074 [INAUDIBLE] shortest path of each of those notes. 660 00:35:33,074 --> 00:35:33,740 PROFESSOR: Good. 661 00:35:33,740 --> 00:35:37,920 So I can look at all the places I could go from s, 662 00:35:37,920 --> 00:35:41,790 and then look at the shortest paths from there to v. 663 00:35:41,790 --> 00:35:45,600 So we could call this s prime. 664 00:35:45,600 --> 00:35:46,730 So here's the idea. 665 00:35:46,730 --> 00:35:50,720 There's some hypothetical shortest path. 666 00:35:50,720 --> 00:35:53,460 I don't know where it goes first, 667 00:35:53,460 --> 00:35:56,080 so I will guess where it goes first. 668 00:35:56,080 --> 00:35:58,800 I know the first edge must be one 669 00:35:58,800 --> 00:36:00,090 of the outgoing edges from s. 670 00:36:00,090 --> 00:36:01,100 I don't know which one. 671 00:36:01,100 --> 00:36:03,210 Try them all. 672 00:36:03,210 --> 00:36:04,479 Very simple idea. 673 00:36:04,479 --> 00:36:06,020 Then from each of those, if somehow I 674 00:36:06,020 --> 00:36:10,590 can compute the shortest path from there to v, 675 00:36:10,590 --> 00:36:13,740 just do that and take the best choice 676 00:36:13,740 --> 00:36:15,650 for what that first edge was. 677 00:36:15,650 --> 00:36:18,670 So this would be the guess first edge approach. 678 00:36:22,340 --> 00:36:23,690 It's a very good idea. 679 00:36:23,690 --> 00:36:28,470 Not quite the one I wanted because unfortunately 680 00:36:28,470 --> 00:36:30,589 that changes s. 681 00:36:30,589 --> 00:36:32,130 And so this would work, it would just 682 00:36:32,130 --> 00:36:33,504 be slightly less efficient if I'm 683 00:36:33,504 --> 00:36:36,090 solving single-source shortest paths. 684 00:36:36,090 --> 00:36:38,010 So I'm going to tweak that idea slightly 685 00:36:38,010 --> 00:36:40,310 by guessing the last edge instead of the first edge. 686 00:36:40,310 --> 00:36:41,962 They're really equivalent. 687 00:36:41,962 --> 00:36:43,420 If I was doing this I'd essentially 688 00:36:43,420 --> 00:36:46,480 be solving a single-target shortest paths, 689 00:36:46,480 --> 00:36:49,820 which we talked about before. 690 00:36:49,820 --> 00:36:51,645 So I'm going to draw the same picture. 691 00:36:56,510 --> 00:36:59,430 I want to get to v. I'm going to guess the last edge, 692 00:36:59,430 --> 00:37:01,560 call it uv. 693 00:37:01,560 --> 00:37:06,260 I know it's one of the incoming edges to v-- unless s equals v, 694 00:37:06,260 --> 00:37:07,780 then there's a special case. 695 00:37:07,780 --> 00:37:09,910 As long as this path has length of at least 1, 696 00:37:09,910 --> 00:37:11,290 there's some last edge. 697 00:37:11,290 --> 00:37:12,040 What is it? 698 00:37:12,040 --> 00:37:12,840 I don't know. 699 00:37:12,840 --> 00:37:14,070 Guess. 700 00:37:14,070 --> 00:37:19,010 Guess all the possible incoming edges to v, and then 701 00:37:19,010 --> 00:37:23,130 recursively compute the shortest path from s to u. 702 00:37:23,130 --> 00:37:25,871 And then add on the edge v. 703 00:37:25,871 --> 00:37:26,370 OK. 704 00:37:26,370 --> 00:37:27,750 So what is this shortest path? 705 00:37:27,750 --> 00:37:32,160 It's delta of s comma u, which looks the same. 706 00:37:32,160 --> 00:37:34,910 It's another subproblem that I want to solve. 707 00:37:34,910 --> 00:37:37,780 There's v subproblems here I care about. . 708 00:37:37,780 --> 00:37:38,660 So that's good. 709 00:37:38,660 --> 00:37:39,780 I take that. 710 00:37:39,780 --> 00:37:41,805 I add on the weight of the edge uv. 711 00:37:44,800 --> 00:37:50,020 And that should hopefully give me delta of s comma v. 712 00:37:50,020 --> 00:37:53,460 Well, if I was lucky and I guessed the right choice of u. 713 00:37:53,460 --> 00:37:55,490 In reality, I'm not lucky. 714 00:37:55,490 --> 00:38:02,790 So I have to minimize over all edges uv. 715 00:38:02,790 --> 00:38:05,370 So this is the-- we're minimizing 716 00:38:05,370 --> 00:38:06,580 over the choice of u. 717 00:38:06,580 --> 00:38:08,420 V is already given here. 718 00:38:08,420 --> 00:38:12,130 So I take the minimum over all edges of the shortest 719 00:38:12,130 --> 00:38:17,324 path from s to u, plus the weight of the edge uv. 720 00:38:17,324 --> 00:38:19,740 That should give me the shortest path because this gave me 721 00:38:19,740 --> 00:38:21,580 the shortest path from s to u. 722 00:38:21,580 --> 00:38:24,110 Then I added on the edge I need to get there. 723 00:38:24,110 --> 00:38:30,120 And wherever the shortest path is, it uses some last edge, uv. 724 00:38:30,120 --> 00:38:32,650 There's got to be some choice of u that is the right one. 725 00:38:32,650 --> 00:38:35,245 That's the good guess that we're hoping for. 726 00:38:35,245 --> 00:38:36,620 We don't know what the good guess 727 00:38:36,620 --> 00:38:38,650 is so we just try them all. 728 00:38:38,650 --> 00:38:43,265 But whatever it is, this will be the weight of that path. 729 00:38:43,265 --> 00:38:44,890 It's going to take the best path from s 730 00:38:44,890 --> 00:38:46,650 to u because sub paths are shortest 731 00:38:46,650 --> 00:38:47,691 paths are shortest paths. 732 00:38:47,691 --> 00:38:48,900 Optimal substructure. 733 00:38:48,900 --> 00:38:51,456 So this part will be delta of su. 734 00:38:51,456 --> 00:38:53,950 This part is obviously w of uv. 735 00:38:53,950 --> 00:38:57,760 So this will give the right answer. 736 00:38:57,760 --> 00:38:59,610 Hopefully. 737 00:38:59,610 --> 00:39:00,865 OK. 738 00:39:00,865 --> 00:39:02,240 It's certainly going to-- I mean, 739 00:39:02,240 --> 00:39:05,100 this is the analog of the naive recursive algorithm 740 00:39:05,100 --> 00:39:05,830 for Fibonacci. 741 00:39:05,830 --> 00:39:08,190 So it's not going to be efficient if I-- I mean, 742 00:39:08,190 --> 00:39:09,850 this is an algorithm, right? 743 00:39:09,850 --> 00:39:13,720 You could say-- this is a recursive call. 744 00:39:13,720 --> 00:39:17,520 We're going to treat this as recursive call instead 745 00:39:17,520 --> 00:39:19,910 of just a definition. 746 00:39:19,910 --> 00:39:23,290 Then this is a recursive algorithm. 747 00:39:23,290 --> 00:39:27,086 How good or bad is this recursive algorithm? 748 00:39:27,086 --> 00:39:28,010 AUDIENCE: Terrible. 749 00:39:28,010 --> 00:39:28,850 PROFESSOR: Terrible. 750 00:39:28,850 --> 00:39:29,960 Very good. 751 00:39:29,960 --> 00:39:31,240 Very bad, I should say. 752 00:39:34,000 --> 00:39:38,080 It's definitely going to be exponential 753 00:39:38,080 --> 00:39:39,120 without memoization. 754 00:39:39,120 --> 00:39:39,957 But we know. 755 00:39:39,957 --> 00:39:41,540 We know how to make algorithms better. 756 00:39:41,540 --> 00:39:42,640 We memoize. 757 00:39:42,640 --> 00:39:43,140 OK. 758 00:39:43,140 --> 00:39:46,850 So I think you know how to write this as a memoized algorithm. 759 00:39:46,850 --> 00:39:51,820 To define the function delta of sv, you first check, 760 00:39:51,820 --> 00:39:54,080 is s comma v in the memo table? 761 00:39:54,080 --> 00:39:55,560 If so return that value. 762 00:39:55,560 --> 00:39:59,450 Otherwise, do this computation where this is a recursive call 763 00:39:59,450 --> 00:40:02,820 and then stored it in the memo table. 764 00:40:02,820 --> 00:40:03,460 OK. 765 00:40:03,460 --> 00:40:05,126 I don't think I need to write that down. 766 00:40:05,126 --> 00:40:07,360 It's just like the memoized code over there. 767 00:40:07,360 --> 00:40:11,190 Just there's now two arguments instead of one. 768 00:40:11,190 --> 00:40:12,390 In fact, s isn't changing. 769 00:40:12,390 --> 00:40:18,500 So I only need to store with v instead of s comma v. 770 00:40:18,500 --> 00:40:19,845 Is that a good algorithm? 771 00:40:19,845 --> 00:40:23,570 I claim memoization makes everything faster. 772 00:40:23,570 --> 00:40:25,700 Is that a fast algorithm? 773 00:40:36,560 --> 00:40:37,640 Not so obvious, I guess. 774 00:40:51,080 --> 00:40:52,570 Yes? 775 00:40:52,570 --> 00:40:54,990 How many people think, yes, that's a good algorithm? 776 00:40:54,990 --> 00:40:55,750 AUDIENCE: Better. 777 00:40:55,750 --> 00:40:56,030 PROFESSOR: Better. 778 00:40:56,030 --> 00:40:57,090 Definitely better. 779 00:40:57,090 --> 00:40:58,340 Can't be worse. 780 00:40:58,340 --> 00:41:00,960 How many people think it's a bad algorithm still? 781 00:41:00,960 --> 00:41:01,460 OK. 782 00:41:01,460 --> 00:41:04,760 So three for yes, zero for no. 783 00:41:04,760 --> 00:41:08,840 How many people aren't sure? 784 00:41:08,840 --> 00:41:09,871 Including the yes votes? 785 00:41:09,871 --> 00:41:10,370 Good. 786 00:41:12,970 --> 00:41:13,570 All right. 787 00:41:13,570 --> 00:41:14,410 It's not so tricky. 788 00:41:14,410 --> 00:41:15,415 Let me draw you a graph. 789 00:41:27,372 --> 00:41:30,350 Something like that. 790 00:41:30,350 --> 00:41:32,330 So we wanted to commit delta of s comma 791 00:41:32,330 --> 00:41:36,670 v. Let me give these guys names, a and b. 792 00:41:36,670 --> 00:41:39,380 So we compute delta of s comma v. To compute 793 00:41:39,380 --> 00:41:45,830 that we need to know delta of s comma a and delta 794 00:41:45,830 --> 00:41:48,110 of s comma v. All right? 795 00:41:48,110 --> 00:41:51,990 Those are the two ways-- sorry, actually we just need one. 796 00:41:51,990 --> 00:41:57,640 Only one incoming edge to v. So its delta of s comma a. 797 00:42:00,950 --> 00:42:04,090 Sorry-- I should have put a base case here too. 798 00:42:04,090 --> 00:42:07,150 Delta of s comma s equals 0. 799 00:42:10,200 --> 00:42:10,700 OK. 800 00:42:10,700 --> 00:42:12,801 Delta of s comma a plus the edge. 801 00:42:12,801 --> 00:42:13,300 OK. 802 00:42:13,300 --> 00:42:14,940 There is some shortest path to a. 803 00:42:14,940 --> 00:42:16,520 To compute the shortest path to a we 804 00:42:16,520 --> 00:42:18,020 look at all the incoming edges to a. 805 00:42:18,020 --> 00:42:19,400 There's only one. 806 00:42:19,400 --> 00:42:22,320 So delta of s comma b. 807 00:42:22,320 --> 00:42:24,620 Now I want to compute the shortest paths from b. 808 00:42:24,620 --> 00:42:26,220 Well, there's two ways to get to b. 809 00:42:26,220 --> 00:42:33,730 One of them is delta of s comma b-- sorry, s comma s. 810 00:42:33,730 --> 00:42:35,470 Came from s. 811 00:42:35,470 --> 00:42:42,760 The other way is delta of s comma v. Do you see a problem? 812 00:42:42,760 --> 00:42:44,540 Yeah. 813 00:42:44,540 --> 00:42:47,420 Delta of s comma v is what we were trying to figure out. 814 00:42:50,080 --> 00:42:51,990 Now you might say, oh, it's OK because we're 815 00:42:51,990 --> 00:42:54,060 going to memoize our answer to delta s comma v 816 00:42:54,060 --> 00:42:55,309 and then we can reuse it here. 817 00:42:55,309 --> 00:42:57,530 Except, we haven't finished computing delta of s 818 00:42:57,530 --> 00:43:02,340 comma v. We can only put it in the memo table once we're done. 819 00:43:02,340 --> 00:43:06,430 So when this call happens the memo table has not been set. 820 00:43:06,430 --> 00:43:07,930 And we're going to do the same thing 821 00:43:07,930 --> 00:43:09,840 over and over and over again. 822 00:43:09,840 --> 00:43:12,890 This is an infinite algorithm. 823 00:43:12,890 --> 00:43:14,740 Oops. 824 00:43:14,740 --> 00:43:15,560 Not so hot. 825 00:43:19,410 --> 00:43:30,160 So it's going to be infinite time on graphs with cycles. 826 00:43:35,155 --> 00:43:35,655 OK. 827 00:43:35,655 --> 00:43:42,400 For DAGs, for acyclic graphs, it actually runs in v plus e time. 828 00:43:42,400 --> 00:43:43,670 This is the good case. 829 00:43:43,670 --> 00:43:46,930 In this situation we can use this formula. 830 00:43:46,930 --> 00:43:48,940 The time is equal to the number of subproblems 831 00:43:48,940 --> 00:43:52,320 times the time per subproblem. 832 00:43:52,320 --> 00:43:55,040 So I guess we have to think about that a little bit. 833 00:43:55,040 --> 00:43:55,720 Where's my code? 834 00:43:55,720 --> 00:43:57,190 Here's my code. 835 00:43:57,190 --> 00:44:00,660 Number of subproblems is v. There's 836 00:44:00,660 --> 00:44:03,110 v different subproblems that I'm using here. 837 00:44:03,110 --> 00:44:05,360 I'm always reusing subproblems of the form delta 838 00:44:05,360 --> 00:44:06,400 s comma something. 839 00:44:06,400 --> 00:44:10,310 The something could be any of the v vertices. 840 00:44:10,310 --> 00:44:14,620 How much time do I spend per subproblem? 841 00:44:14,620 --> 00:44:15,790 That's a little tricky. 842 00:44:15,790 --> 00:44:17,640 It's the number of incoming edges 843 00:44:17,640 --> 00:44:30,710 to v. So time for a sub problem delta of sv 844 00:44:30,710 --> 00:44:36,850 is the indegree of v. The number of incoming edges to v. 845 00:44:36,850 --> 00:44:39,230 So this depends on v. So I can't just 846 00:44:39,230 --> 00:44:41,050 take a straightforward product here. 847 00:44:41,050 --> 00:44:42,466 What this is really saying is, you 848 00:44:42,466 --> 00:44:44,130 should sum up over all sub problems 849 00:44:44,130 --> 00:44:46,600 of the time per sub problem. 850 00:44:46,600 --> 00:44:58,360 So total time is the sum over all v and v, the indegree of v. 851 00:44:58,360 --> 00:45:02,920 And we know this is number of edges. 852 00:45:02,920 --> 00:45:06,650 It's really-- so indegree plus 1, indegree plus 1. 853 00:45:06,650 --> 00:45:11,000 So this is v plus v. OK. 854 00:45:11,000 --> 00:45:14,060 Handshaking again. 855 00:45:14,060 --> 00:45:14,680 OK. 856 00:45:14,680 --> 00:45:17,340 Now we already knew an algorithm for shortest paths and DAGs. 857 00:45:17,340 --> 00:45:18,650 And it ran a v plus e time. 858 00:45:18,650 --> 00:45:21,370 So it's another way to do the same thing. 859 00:45:21,370 --> 00:45:23,930 If you think about it long enough, 860 00:45:23,930 --> 00:45:27,340 this algorithm memoized, is essentially 861 00:45:27,340 --> 00:45:30,770 doing a depth first search to do a topological sort 862 00:45:30,770 --> 00:45:33,440 to run one round of Bellman-Ford. 863 00:45:33,440 --> 00:45:36,120 So we had topological sort plus one round of Bellman-Ford. 864 00:45:36,120 --> 00:45:38,160 This is kind of it all rolled into one. 865 00:45:38,160 --> 00:45:40,630 This should look kind of like the Bellman Ford relaxation 866 00:45:40,630 --> 00:45:42,960 step, or shortest paths relaxation step. 867 00:45:42,960 --> 00:45:44,150 It is. 868 00:45:44,150 --> 00:45:46,724 This min is really doing the same thing. 869 00:45:46,724 --> 00:45:48,140 So it's really the same algorithm. 870 00:45:48,140 --> 00:45:50,098 But we come at it from a different perspective. 871 00:45:52,400 --> 00:45:53,470 OK. 872 00:45:53,470 --> 00:45:55,870 But I claim I can use this same approach 873 00:45:55,870 --> 00:45:58,830 to solve shortest paths in general graphs, even when they 874 00:45:58,830 --> 00:46:01,540 have cycles. 875 00:46:01,540 --> 00:46:04,170 How am I going to do that? 876 00:46:04,170 --> 00:46:08,980 DAGs seem fine-- oh, what was the lesson learned here? 877 00:46:08,980 --> 00:46:18,400 Lesson learned is that subproblem dependencies 878 00:46:18,400 --> 00:46:19,450 should be acyclic. 879 00:46:19,450 --> 00:46:22,240 Otherwise, we get an infinite algorithm. 880 00:46:22,240 --> 00:46:24,990 For memoization to work this is what you need. 881 00:46:24,990 --> 00:46:26,050 It's all you need. 882 00:46:29,590 --> 00:46:30,090 OK. 883 00:46:30,090 --> 00:46:32,460 We've almost seen this already. 884 00:46:32,460 --> 00:46:35,090 Because I said that, to do a bottom up algorithm 885 00:46:35,090 --> 00:46:39,140 you do a topological sort of this subproblem dependency DAG. 886 00:46:39,140 --> 00:46:40,641 I already said it should be acyclic. 887 00:46:40,641 --> 00:46:41,140 OK. 888 00:46:41,140 --> 00:46:42,490 We just forgot. 889 00:46:42,490 --> 00:46:43,872 I didn't tell you yet. 890 00:46:43,872 --> 00:46:45,580 So for that to work it better be acyclic. 891 00:46:45,580 --> 00:46:48,870 For DP to work, for memoization to work, it better be acyclic. 892 00:46:48,870 --> 00:46:55,530 If you're acyclic then this is the running time. 893 00:46:55,530 --> 00:46:59,590 So that's all general. 894 00:46:59,590 --> 00:47:00,090 OK. 895 00:47:00,090 --> 00:47:02,060 So somehow I need to take a cyclic graph 896 00:47:02,060 --> 00:47:03,005 and make it acyclic. 897 00:47:08,047 --> 00:47:10,005 We've actually done this already in recitation. 898 00:47:18,740 --> 00:47:22,830 So if I have a graph-- let's take 899 00:47:22,830 --> 00:47:25,850 a very simple cyclic graph. 900 00:47:25,850 --> 00:47:26,350 OK. 901 00:47:26,350 --> 00:47:29,570 One thing I could do is explode it into multiple layers. 902 00:47:29,570 --> 00:47:32,400 We did this on quiz two in various forms. 903 00:47:32,400 --> 00:47:35,020 It's like the only cool thing you can do with shortest paths, 904 00:47:35,020 --> 00:47:37,882 I feel like. 905 00:47:37,882 --> 00:47:40,200 If you want to make a shortest path problem harder, 906 00:47:40,200 --> 00:47:45,597 require that you reduce your graph to k copies of the graph. 907 00:47:45,597 --> 00:47:47,180 I'm going to do it in a particular way 908 00:47:47,180 --> 00:47:50,580 here-- which I think you've seen in recitation-- which 909 00:47:50,580 --> 00:47:54,360 is to think of this axis as time, or however you want, 910 00:47:54,360 --> 00:47:57,090 and make all of the edges go from each layer 911 00:47:57,090 --> 00:47:58,050 to the next layer. 912 00:48:01,080 --> 00:48:03,470 This should be a familiar technique. 913 00:48:03,470 --> 00:48:05,340 So the idea is, every time I follow 914 00:48:05,340 --> 00:48:07,440 an edge I go down to the next layer. 915 00:48:07,440 --> 00:48:09,740 This makes any graph acyclic. 916 00:48:09,740 --> 00:48:11,510 Done. 917 00:48:11,510 --> 00:48:13,640 What in the world does this mean? 918 00:48:13,640 --> 00:48:14,550 What is it doing? 919 00:48:20,070 --> 00:48:21,960 What does it mean? 920 00:48:21,960 --> 00:48:23,100 Double rainbow. 921 00:48:23,100 --> 00:48:23,600 All right. 922 00:48:23,600 --> 00:48:24,433 AUDIENCE: [LAUGHTER] 923 00:48:24,433 --> 00:48:26,694 PROFESSOR: So-- I don't know how I've 924 00:48:26,694 --> 00:48:28,610 gone so long in the semester without referring 925 00:48:28,610 --> 00:48:29,360 to double rainbow. 926 00:48:29,360 --> 00:48:30,680 It used to be my favorite. 927 00:48:30,680 --> 00:48:31,180 All right. 928 00:48:31,180 --> 00:48:34,570 So here's what it means. 929 00:48:34,570 --> 00:48:37,019 Delta sub k of sv. 930 00:48:37,019 --> 00:48:38,560 I'm going to define this first-- this 931 00:48:38,560 --> 00:48:41,640 is a new kind of subproblem-- which 932 00:48:41,640 --> 00:48:49,220 is, what is the shortest-- what is the weight of the shortest 933 00:48:49,220 --> 00:48:54,525 s to v path that uses, at most, k edges. 934 00:48:58,732 --> 00:49:00,940 So I want it to be shortest in terms of total weight, 935 00:49:00,940 --> 00:49:03,090 but I also want it to use few edges total. 936 00:49:03,090 --> 00:49:05,200 So this is going to be 0. 937 00:49:05,200 --> 00:49:08,580 In some sense, if you look at-- so here's s 938 00:49:08,580 --> 00:49:11,890 and I'm always going to make s this. 939 00:49:11,890 --> 00:49:15,370 And then this is going to be v in the zero situation. 940 00:49:15,370 --> 00:49:17,460 This is going to be v in the one situation, 941 00:49:17,460 --> 00:49:20,600 v-- so if I look at this v, I look at the shortest 942 00:49:20,600 --> 00:49:24,040 path from s to v, that is delta sub 0 of sv. 943 00:49:24,040 --> 00:49:28,460 So maybe I'll call this v sub 0, v sub 1, v sub 2. 944 00:49:28,460 --> 00:49:28,960 OK. 945 00:49:28,960 --> 00:49:30,585 Shortest path from here to here is, 946 00:49:30,585 --> 00:49:32,390 there's no way to get there on 0 edges. 947 00:49:32,390 --> 00:49:33,970 Shortest path from here to here, that 948 00:49:33,970 --> 00:49:38,166 is the best way to get there with, at most, one edge. 949 00:49:38,166 --> 00:49:39,540 Shortest path from here to here-- 950 00:49:39,540 --> 00:49:41,770 well, if I add some vertical edges too, 951 00:49:41,770 --> 00:49:43,436 I guess, cheating a little bit. 952 00:49:43,436 --> 00:49:45,060 Then this is the best way to get from s 953 00:49:45,060 --> 00:49:46,960 to v using at most two edges. 954 00:49:46,960 --> 00:49:51,000 And then you get a recurrence which 955 00:49:51,000 --> 00:49:54,750 is the min over all last edges. 956 00:49:54,750 --> 00:49:56,570 So I'm just copying that recurrence, 957 00:49:56,570 --> 00:50:03,040 but realizing that the s to u part uses one fewer edge. 958 00:50:03,040 --> 00:50:04,331 And then I use the edge uv. 959 00:50:07,198 --> 00:50:07,698 OK. 960 00:50:07,698 --> 00:50:09,330 That's our new recurrence. 961 00:50:09,330 --> 00:50:11,380 By adding this k parameter I've made 962 00:50:11,380 --> 00:50:14,180 this recurrence on subproblems acyclic. 963 00:50:14,180 --> 00:50:17,050 Unfortunately, I've increased the number of subproblems. 964 00:50:17,050 --> 00:50:28,250 The number of subproblems now is v squared. 965 00:50:28,250 --> 00:50:30,840 Technically, v times v minus 1. 966 00:50:30,840 --> 00:50:32,880 Because I really-- actually, v squared. 967 00:50:32,880 --> 00:50:34,130 Sorry. 968 00:50:34,130 --> 00:50:36,860 I start at 0. 969 00:50:36,860 --> 00:50:46,210 And what I care about, my goal, is delta sub v minus 1 of sv. 970 00:50:46,210 --> 00:50:47,900 Because by Bellman-Ford analysis I 971 00:50:47,900 --> 00:50:51,270 know that I only care about simple paths, paths of length 972 00:50:51,270 --> 00:50:52,040 at most v minus 1. 973 00:50:52,040 --> 00:50:53,873 I'm assuming here no negative weight cycles. 974 00:50:53,873 --> 00:50:55,450 I should've said that earlier. 975 00:50:55,450 --> 00:50:58,620 If you assume that, then this is what I care about. 976 00:50:58,620 --> 00:51:00,380 So k ranges from 0 to v minus 1. 977 00:51:00,380 --> 00:51:02,160 So there are v choices for k. 978 00:51:02,160 --> 00:51:04,860 There are v choices for v. So the number of subproblems 979 00:51:04,860 --> 00:51:05,510 is v squared. 980 00:51:05,510 --> 00:51:07,230 How much time do I spend per subproblem? 981 00:51:07,230 --> 00:51:08,271 Well, the same as before. 982 00:51:08,271 --> 00:51:09,890 The indegree-- where did I write it? 983 00:51:09,890 --> 00:51:13,012 Up here-- the indegree of that problem. 984 00:51:13,012 --> 00:51:14,470 So what I'm really doing is summing 985 00:51:14,470 --> 00:51:17,400 over all v of the indegree. 986 00:51:17,400 --> 00:51:19,850 And then I multiply it by v. So the running time, 987 00:51:19,850 --> 00:51:25,240 total running time is ve. 988 00:51:25,240 --> 00:51:26,740 Sound familiar? 989 00:51:26,740 --> 00:51:29,660 This is Bellman-Ford's algorithm again. 990 00:51:29,660 --> 00:51:32,410 And this is actually where Bellman-Ford algorithm 991 00:51:32,410 --> 00:51:35,810 came from is this view on dynamic programming. 992 00:51:35,810 --> 00:51:38,640 So we're seeing yet another way to do Bellman-Ford. 993 00:51:38,640 --> 00:51:39,660 It may seem familiar. 994 00:51:39,660 --> 00:51:41,160 But in the next three lectures we're 995 00:51:41,160 --> 00:51:42,743 going to see a whole bunch of problems 996 00:51:42,743 --> 00:51:44,650 that can succumb to the same approach. 997 00:51:44,650 --> 00:51:47,040 And that's super cool.