1 00:00:04,600 --> 00:00:10,150 There are many ways to complete a task---even a seemingly simple one like eating cereal. 2 00:00:10,150 --> 00:00:14,549 When programming a computer to complete a task or solve a problem, repetitive techniques 3 00:00:14,549 --> 00:00:19,650 like iteration and recursion are extremely useful. In this video, we will look at these 4 00:00:19,650 --> 00:00:21,870 problem-solving techniques. 5 00:00:21,870 --> 00:00:27,520 This video is part of the Problem Solving video series. Problem-solving skills, in combination 6 00:00:27,520 --> 00:00:33,280 with an understanding of the natural and human-made world, are critical to the design and optimization 7 00:00:33,280 --> 00:00:34,949 of systems and processes. 8 00:00:34,949 --> 00:00:41,260 Hi, my name is Niaja Farve. I am a doctoral student in Electrical Engineering and Computer 9 00:00:41,260 --> 00:00:43,679 Science at MIT. 10 00:00:43,679 --> 00:00:48,019 Before watching this video, you should be familiar with introductory programming and 11 00:00:48,019 --> 00:00:50,469 simple data structures. 12 00:00:50,469 --> 00:00:55,979 After watching this video, you will be able to: Divide a programming problem into simpler, 13 00:00:55,979 --> 00:01:02,979 analogous pieces. And, solve the problem by combining solutions to the simpler pieces. 14 00:01:07,440 --> 00:01:12,850 In computer science, we often want to solve complex problems. However, computers deal 15 00:01:12,850 --> 00:01:19,380 best with performing easy tasks over and over again. We utilize the computer's ability by 16 00:01:19,380 --> 00:01:24,908 implementing repetitive techniques to incrementally solve our complex problems. 17 00:01:24,908 --> 00:01:30,600 Though eating a bowl of cereal is a fairly simple task that most of us can complete automatically, 18 00:01:30,600 --> 00:01:35,880 we need to think carefully about how to program a computer to do the same. Let's take a closer 19 00:01:35,880 --> 00:01:42,100 look at the problem and identify the fundamental steps used to frame cereal eating for repetitive 20 00:01:42,100 --> 00:01:43,100 computation. 21 00:01:43,100 --> 00:01:47,658 In this example, let's suppose the computer understands the basic operation of eating 22 00:01:47,658 --> 00:01:53,929 a single bite of cereal, but does not understand how to eat an entire bowl of cereal. So how 23 00:01:53,929 --> 00:01:59,479 do we "teach" the computer to eat a bowl of any size greater than one bite? Pause the 24 00:01:59,479 --> 00:02:06,479 video here and think about it. 25 00:02:07,770 --> 00:02:11,930 The first step when approaching a complex programming problem is to break the problem 26 00:02:11,930 --> 00:02:17,750 up into analogous, but simpler pieces that we can tell the computer how to directly solve. 27 00:02:17,750 --> 00:02:22,350 So what is a simpler version of eating a whole bowl of cereal? 28 00:02:22,350 --> 00:02:26,480 One possibility is eating a smaller amount of cereal. 29 00:02:26,480 --> 00:02:31,300 We already mentioned that a small, non-zero amount of cereal the computer can handle eating 30 00:02:31,300 --> 00:02:34,220 is a single bite. 31 00:02:34,220 --> 00:02:39,430 Going one step up, eating two bites-worth of cereal is equivalent to eating a single 32 00:02:39,430 --> 00:02:46,210 bite twice. The next step when approaching a complex problem is to deduce a pattern. 33 00:02:46,210 --> 00:02:51,480 That is, how does a generally larger problem look in comparison to the simpler version? 34 00:02:51,480 --> 00:02:56,260 Here, we notice that eating any amount of cereal is equivalent to the sum of eating 35 00:02:56,260 --> 00:02:59,230 multiple bites-worth of cereal. 36 00:02:59,230 --> 00:03:04,740 Now that we've broken up our problem and understand how the pieces will fit back together, we 37 00:03:04,740 --> 00:03:09,180 can put our solution into a generalized code framework: 38 00:03:09,180 --> 00:03:14,120 Start with telling the computer the procedure for solving the simplest problem. Then, repeat 39 00:03:14,120 --> 00:03:19,130 this procedure on subsequent pieces until the desired endpoint is reached: 40 00:03:19,130 --> 00:03:26,130 If the bowl contains cereal, take one bite of cereal. Repeat until there is no more cereal. 41 00:03:31,540 --> 00:03:36,250 You may recognize this type of solution as an iterative approach. 42 00:03:36,250 --> 00:03:39,810 Or we could also take the following alternative approach: 43 00:03:39,810 --> 00:03:44,500 Start with telling the computer how to solve the simplest problem. Then, break the problem 44 00:03:44,500 --> 00:03:49,320 into simpler and simpler pieces until we reach the version we've already told the computer 45 00:03:49,320 --> 00:03:54,840 how to solve. Does the bowl contain 1 bite of cereal? If 46 00:03:54,840 --> 00:04:01,030 so, take the bite. If not, divide it up into a bowl containing one bite and a bowl containing 47 00:04:01,030 --> 00:04:06,090 the remainder. Repeat this procedure on the resulting bowls. 48 00:04:06,090 --> 00:04:11,040 In this case, we end, up with a series of bowls containing one bite, which the computer 49 00:04:11,040 --> 00:04:13,200 knows how to eat. 50 00:04:13,200 --> 00:04:17,880 Breaking up a problem into progressively simpler, but analogous pieces in this way is known 51 00:04:17,880 --> 00:04:23,350 as a recursive approach. Because the solution to the most complex problem depends on solutions 52 00:04:23,350 --> 00:04:28,930 to the simpler pieces, recursion creates a queue of jobs waiting to be completed. 53 00:04:28,930 --> 00:04:35,260 In contrast, when using iteration, there is no such dependency from one instance of the 54 00:04:35,260 --> 00:04:37,260 problem to the next. 55 00:04:37,260 --> 00:04:43,030 So even though the computer ends up consuming n bites of cereal in both cases, the iterative 56 00:04:43,030 --> 00:04:48,150 and recursive approaches arrive at this answer in very different ways. 57 00:04:48,150 --> 00:04:53,690 There are many different ways to successfully eat a bowl of cereal or solve any given programming 58 00:04:53,690 --> 00:05:00,380 problem. The key is to 1. Break the problem into analogous pieces that the computer can 59 00:05:00,380 --> 00:05:05,790 solve, and 2. Combine the solutions to the pieces into a solution for the more complex 60 00:05:05,790 --> 00:05:11,620 problem. 61 00:05:11,620 --> 00:05:17,470 In our previous cereal-eating example, breaking down the problem into simpler pieces was fairly 62 00:05:17,470 --> 00:05:21,260 straightforward. Now, let's look at a slightly more complex problem. 63 00:05:21,260 --> 00:05:27,250 We would like to write a function, downup, that takes an input string and prints out 64 00:05:27,250 --> 00:05:32,260 progressively smaller and larger sub-strings of the word as so. 65 00:05:32,260 --> 00:05:38,190 To help us work with strings, we have a helper function, substring, which extracts a portion 66 00:05:38,190 --> 00:05:44,440 of a string, beginning from the first character up though a specified end index. 67 00:05:44,440 --> 00:05:48,900 Following the framework we discussed earlier, what is a simpler version of downup that we 68 00:05:48,900 --> 00:05:50,980 can easily handle? 69 00:05:50,980 --> 00:05:56,460 How about downup of a single letter string. The desired output is achieved by simply printing 70 00:05:56,460 --> 00:05:58,500 the string. 71 00:05:58,500 --> 00:06:03,530 Moving one step up, we see that the desired output of a two-letter string can be accomplished 72 00:06:03,530 --> 00:06:08,070 by printing the string, printing the string shortened by one letter, and printing the 73 00:06:08,070 --> 00:06:11,120 full string a second time. 74 00:06:11,120 --> 00:06:16,650 And moving up one more step to a 3 letter string... 75 00:06:16,650 --> 00:06:23,650 Are you starting to notice any patterns? Pause the video to think of a possibility. 76 00:06:27,100 --> 00:06:31,370 Though there are many different patterns, here is one you may have come up with: With 77 00:06:31,370 --> 00:06:36,740 every string, we are sandwiching the _solution_ to a string one character shorter between 78 00:06:36,740 --> 00:06:42,490 two printings of the full string. With this mindset, we are poised for a recursive solution 79 00:06:42,490 --> 00:06:44,470 to this problem. 80 00:06:44,470 --> 00:06:49,680 Recall the general framework for a recursive solution: Tell the computer how to solve the 81 00:06:49,680 --> 00:06:56,199 simplest problem. Then break the problem into simpler pieces until we reach the simplest 82 00:06:56,199 --> 00:07:02,650 problem: If the string is a single letter, print it. Otherwise, print the string, solve 83 00:07:02,650 --> 00:07:09,010 for downup of the string one character shorter, and print the string again. 84 00:07:09,010 --> 00:07:15,180 Try now, if you haven't already done so, to frame the problem in a more iterative manner. 85 00:07:15,180 --> 00:07:22,180 Pause the video to discuss. 86 00:07:23,770 --> 00:07:29,240 We can notice that we are repeatedly printing substrings of the full string, with each step 87 00:07:29,240 --> 00:07:35,639 moving the end index from the original length down to 1. This is followed by again printing 88 00:07:35,639 --> 00:07:41,840 substrings, but this time increasing the end index back up to the original length. 89 00:07:41,840 --> 00:07:48,840 We can program this solution using two iterative loops. Recall the general iterative code framework: 90 00:07:48,979 --> 00:07:54,110 Tell the computer the procedure for the simplest problem. Repeat the procedure on subsequent 91 00:07:54,110 --> 00:07:57,460 pieces until the endpoint is reached. 92 00:07:57,460 --> 00:08:02,490 In our first loop, we print the substring, decrease the index by one, and repeat the 93 00:08:02,490 --> 00:08:06,490 procedure until the index reaches one. 94 00:08:06,490 --> 00:08:12,130 In the second loop, we move in the opposite direction. Print the substring. Increase the 95 00:08:12,130 --> 00:08:18,460 index by one and repeat the procedure until the index is greater than the original length. 96 00:08:18,460 --> 00:08:24,040 Both approaches, while very different, are completely valid! There is no one correct 97 00:08:24,040 --> 00:08:31,040 way to solve a problem. Some solutions may even have both recursive and iterative elements. 98 00:08:36,059 --> 00:08:40,698 Now lets solve a third problem with an even more complex pattern. 99 00:08:40,698 --> 00:08:45,589 In the famous Towers of Hanoi problem, the goal is to transfer a stack of rings from 100 00:08:45,589 --> 00:08:52,589 pillar A to pillar C. We can only move a single ring at a time, and can use pillar B as "extra" 101 00:08:54,050 --> 00:08:59,959 workspace. We cannot place a larger ring on top of a smaller ring. 102 00:08:59,959 --> 00:09:06,369 Lets follow the framework and start by working out the simplest problem: transferring 1 ring. 103 00:09:06,369 --> 00:09:09,119 Here we can simply move the ring from A to C. 104 00:09:09,119 --> 00:09:16,119 Okay, now let's try to transfer a stack of two rings. First we move the ring 1 to B, 105 00:09:17,550 --> 00:09:22,410 then ring 2 to C, then move ring 1 from B to C. 106 00:09:22,410 --> 00:09:29,410 Now, let's try something a bit harder. Let's try to transfer a stack of three rings. Ring 107 00:09:30,550 --> 00:09:37,550 1 moves to C, ring 2 goes to B, and ring 1 goes to B. Now ring 3, which was on the bottom, 108 00:09:40,889 --> 00:09:47,889 is free to move to C. Then, ring 1 goes to A, ring 2 goes to C, then ring 1 finally goes 109 00:09:49,470 --> 00:09:50,939 to C. 110 00:09:50,939 --> 00:09:57,369 Notice that after we moved the bottom ring to C, we essentially arrived at the same conformation 111 00:09:57,369 --> 00:10:03,959 as when trying to transfer 2 rings: We have two stacked rings and an extra empty pillar. 112 00:10:03,959 --> 00:10:09,449 And because the largest ring is in the desired, final position and does not impede the movements 113 00:10:09,449 --> 00:10:15,459 of any of the remaining smaller rings, we can treat the pillar as being empty. 114 00:10:15,459 --> 00:10:22,249 This observation is crucial to the recursive implementation of the towers of Hanoi solution. 115 00:10:22,249 --> 00:10:29,059 We transferred n-1 rings to the extra pillar, moved the largest ring to the final position, 116 00:10:29,059 --> 00:10:33,399 then transferred the n-1 rings to the final position. 117 00:10:33,399 --> 00:10:38,529 Can you frame a pseudocode solution to the problem? Pause the video here to work out 118 00:10:38,529 --> 00:10:45,529 a possible solution. 119 00:10:46,589 --> 00:10:52,509 Recall again the general recursive framework: Tell the computer how to solve the simplest 120 00:10:52,509 --> 00:10:59,259 problem: If we're transferring a single ring, move it to the destination pillar. 121 00:10:59,259 --> 00:11:05,579 Then, break the problem up into progressively simpler pieces: 122 00:11:05,579 --> 00:11:11,929 Transfer n-1 rings from the source to the extra pillar, transfer ring n from the source 123 00:11:11,929 --> 00:11:18,929 to the destination, and transfer n-1 rings from the extra pillar to the destination. 124 00:11:19,279 --> 00:11:24,399 Note that the source, destination, and extra pillar designations change with each function 125 00:11:24,399 --> 00:11:25,569 call! 126 00:11:25,569 --> 00:11:31,139 It's always a good idea to check your code with a test case. Pause the video here and 127 00:11:31,139 --> 00:11:37,990 check your code for the case of N equals 4. You may also wish to check our code and compare 128 00:11:37,990 --> 00:11:44,990 the two solution methods. 129 00:11:46,529 --> 00:11:51,420 Now lets check our code and traverse through the solution for transferring 4 rings. 130 00:11:51,420 --> 00:11:58,189 We're not transferring a single ring, so lets transfer 3 rings from A to B. 131 00:11:58,189 --> 00:12:04,379 We're still not transferring one ring, so lets transfer 2 rings from A to C. 132 00:12:04,379 --> 00:12:10,569 We're still not transferring one ring, so lets transfer 1 ring from A to B. And we can 133 00:12:10,569 --> 00:12:14,179 finally move this single ring! 134 00:12:14,179 --> 00:12:21,179 Now we return to the previous call, and can move ring 2 from A to C. Then we transfer 135 00:12:21,309 --> 00:12:28,309 our n-1 stack from B to C. This results in transferring 2 rings from A to C! 136 00:12:32,749 --> 00:12:39,749 Returning one more step back, we move ring 3 to B. Now we transfer 2 rings from C to 137 00:12:40,839 --> 00:12:47,839 A. 138 00:12:48,379 --> 00:12:55,379 Finally, we've returned back to our original function call, and we've completed transferring 139 00:13:00,220 --> 00:13:07,220 3 rings from A to B. So, we can move our largest ring number 4 from A to C. 140 00:13:10,420 --> 00:13:16,600 Now we need a whole other set of recursive function calls to transfer the 3 ring stack 141 00:13:16,600 --> 00:13:22,480 from B to C! Pause the video now to finish checking the second half of our solution. 142 00:13:22,480 --> 00:13:29,480 Notice how quickly the number of function calls grew! Good thing we have a computer 143 00:13:30,170 --> 00:13:34,269 that is very good at following repetitive instructions! 144 00:13:34,269 --> 00:13:39,579 To Review In this video, we showed you how recursion and iteration take advantage of 145 00:13:39,579 --> 00:13:43,550 a computer's ability to repeat simple tasks. 146 00:13:43,550 --> 00:13:48,889 To approach a complicated programming problem, first solve some simpler versions and try 147 00:13:48,889 --> 00:13:54,470 to identify a pattern. Then, depending on the type of pattern you found, fill in a recursive 148 00:13:54,470 --> 00:14:00,679 or iterative code framework. And remember, iterative, recursive and even mixed solutions 149 00:14:00,679 --> 00:14:07,679 to a single problem may all be correct!