 All righty, welcome back to 105. So we get to do some more recursion today. So just as a reminder, recursion, kind of a hard topic to wrap your head around, but really just two rules. So a recursive function is a function that calls itself and we need two things in order for it to be useful and to actually give us some result. So today I have some props, so we get to figure out a fun toy, but before that we'll go over some more problems. So we need two things. We need first our base case, so that is a simple solution that we know, and then we do a recursive step. So we reduce the problem and solve it in terms of a smaller version of itself and those are the only two things. We don't have to, for some problems, we don't have to think more than that if the solution is really, really complicated. All we need is these two things and then we actually have a solution for it. So another thing we could do is compute the greatest common divisor, typically another prime example of using recursion. So the greatest common divisor or GCD of two integers, A and B, is just the largest integer D, that is a divisor of both A and B. And for the purposes of this, we'll just assume that all the integers are positive and greater than zero. So there is a algorithm for this called the Euclidean algorithm, and it says to find the largest common divisor between two integers, given that A is greater than or equal to B, all we have to do is to find the greatest common divisor of A and B, just replace it with B as the first argument and then the second one could just be A mod B. So basically we get the remainder of the larger number and we just recursively do that over and over and over again until eventually one of the arguments becomes zero. So we have a zero divisor, which means that while it divides evenly and then that remaining number would be our greatest common divisor. Don't worry about this algorithm, this is all in the math, we will just worry about actually implementing this so we can actually write a recursive solution to this problem. So what it would look like is, well, let's go there. So we can just go ahead and implement it directly. So if we're doing the greatest common divisor of A and B and we know that B is equal to zero, then that is our base case. So we can just return A. So A is actually D, which would be our greatest common divisor. Now this is a case where A is greater than or equal to B, then we can just return the greatest common divisor of, well, we switch the arguments. So the first argument becomes the current value of B and then the new value of B becomes A mod B. So we also have to worry about the case where A is less than B and for that, well, we can simply just do else and then return and just swap the two arguments around. So we make sure that if A is smaller than B, we'll just swap the arguments around and then suddenly it will become, this will be true because, well, now A will be greater than or equal to B. So for the rest of it, I use the other version of main because I don't want to have two scan Fs and hit enter a bunch of times because I'm lazy. So I just check that I have three arguments. So the program name and then two numbers that I'll give to A to I, which is our string function that converts the string to a natural integer. I'll check that they are both greater than zero. So if either is equal to zero or less than, I will just exit with an exit failure. Then other than that, I will just return the greatest common divisor. So if we think about the greatest common divisor between like 20 and eight, well, common divisors between both numbers are one, two. So two can divide evenly for that and then four and then that's it. So our common divisors are one, two and four. So with this algorithm, we should get the result four because that is the greatest common divisor. So if we run it, we get four. So fun, we can just implement it, get the greatest common divisor. Works for arguments if we reverse it to. So if we reverse it, we get, go ahead and we get, the first call is A is eight and B is 20, but then we would go into this else statement. It would go ahead, swap them around and now we would get what we had before. So how this would work is if we call GCD 20 and then eight, what's going to happen is in this case, while 20 is greater than or equal to B, so we would do swap them and then this would evaluate GCD of eight and then we would do 20 mod eight. So what's 20 mod eight quick? Four, all right, so it's just four. So this would evaluate to four. So in order to do GCD of eight and four, well, we swap the arguments, we get four and then we would get eight mod four, which is just simply equal to zero and then, well, whatever we call this function, this would hit our base case. So this would just go ahead and directly return four and then we go ahead, we get four as our final answer. Why this works, you can ask math people, but this is how our computer would actually go ahead and evaluate all of these. So let us get into some more examples. So here's that, so you can have it. For more practice, if you want, we could write this in terms of a loop as well. So you could try and solve this using in terms of a loop if you just want to get some more practice with C and converting these two things and just thinking about solving the same problem in the same way. So another thing we could do is, well, recursion looks a lot like loops. So maybe we try to count to five recursively. So let's stop and think for a minute that if I was to use recursion, how could I actually count to five recursively? So I only want to do one step at a time and then aside from that, I should call myself. So let's think of the two things we need. We need a base case and then we need to just solve a single step of it. So I will give you about a minute or two and then we can, yeah, a minute or two a think, then we can try and implement it. And as a hint, we'd probably just want to write a function that returns nothing and we just give it an int of the number of numbers to count. So it should print out like hopefully one, two, three, four, five, if we give it a count of five. All right, any thoughts? So what is a, well, what would be a good base case in this scenario? If I want to count, if I give it n of five, I want it to count down like one, two, three, four, five and that's it. Yeah, so n equals one as a base case. So if n is one, what do I want to do? So, well, in this case, I just want print, right? So just print n, okay, and then print and then return so that I'm done. So this would be my base case. Okay, then what would I want to do as my recursive step? Anyone for the recursive step then? So just count n minus one. So I should just count one less and that should work. Yeah, print, where do you want me to print? Like, oops, so print here. Do you want me to just put like the count thing here? So that's gonna be the same as if I do, well, count doesn't actually return anything, right? So if I put count here, it doesn't have a value so it's not gonna work. So I have to do something like that. So that, okay, well, let's try it. So build, boom. So yep, one, two, three, four, five, six, seven all the way up to five. So what we could do instead, if we want to get rid of one print f, well, we could think of our base case as just n is equal to zero then we don't have to do anything. So we could just return and now we do that. So we call count of five. Well, in order to do count of five, we have to count to four first. To count to four first, we have to count to three, then we have to count to two, then we have to count to one and then we have to count to zero which won't do anything. And then we'll go ahead and then print one, two, three, four, five. So this is pretty much exactly the same as just doing a while loop. The only thing that might be confusing is what happens if I do that instead? So if I move the print before the recursive call. Yeah. Do it backwards. Do it backwards, right? So in this case, if I compile it, I run it, now it's just backwards. So I get five first, then four, then three, then two, then one. And why is that? Well, I do the print F before I do the recursive step. So if I call count of five, well, first thing it's going to do is go into the recursive step, then print out five first before it recursively calls itself. That would call count of four. So that would go into the recursive step. It would print four, then do count three. Then count three would return, or print three, then it does count two. Print's two, does count one, prints one. Then it calls count zero, and then count zero does nothing. And then the whole thing just returns and returns nothing because it is a void function. So everyone okay with that? So print F, so with recursion gets a bit weird. You have to think about what happens in what order because just moving a line by one suddenly just changes counting from five to one to one to five. So this will become more important later, but I'm showing you this now just so you can kind of wrap your head around what happens if I just move a print F line if I'm calling a function recursively. So do, do, do. So here is the solution to you. I just remove the else statement. Otherwise it's exactly the same. So what about computing the sum of an array recursively? Ooh, can we do that as more practice? So let's think about how we would write a function to compute the sum of an array using recursion. So I'll do this. So we've wrote this function before using a while loop or a four, sorry, a four loop, but now we can go ahead and just practice writing it recursively. And good to get into this mind of thinking because when we try to solve a baby's game we will need to think recursively. So let's think about how we would calculate the sum of an array using recursion. So I will give you a minute or two to ponder this and then we will try and do it. And it's just going to be an integer array. So if I wanted to, I could write it like this, but remember array decays a thing means the same thing. I'll leave as a pointer. All right, anyone with an idea or a solution to this? Yeah, so my base case could be if the array length is one. Oops, if the array length, all right, let's do some other practice. If I do this, is that smart? No, that is assignment, not a equivalent. So here, so this is my base case. Here I'll go ahead and write in a comment base case. So if my array length is one, what should I do? Just the returns of value of the array. So what would the first element, which would be index what? Zero. So just return index zero. Okay, sure. Okay, then what about my recursive step? So what should my recursive step be? Recursive step. So how do I solve it in terms of itself? Yeah. Yeah, so use the first element and then, well, I need to return. Yeah, so I could do first or second. So if I start with the first, let's just say we start with the first, what should my smaller problem be? Yeah, so I can use pointer arithmetic here. So if I do array plus one, that will go to the next element. So I'm adding this number and then I'm trying to add a smaller array. So I'm doing the sum of the remaining array minus the first element. So that would be array plus one to move that pointer forward one. What should I do with array length? Yeah, array length also needs to go down by one because I'm making my array one smaller. I'm moving an element, so I also need to adjust the array length. So, and then I need to make sure that I go ahead and I return something too. So let's see if that works. So let's compile. So in my main, my array just has the elements one, two, three, four, five. If I add them all together, it should be 15 if we have written our program correctly. Then I use that funny array length macro I had before because while it is defined and declared in the main function, so array length would work. Array length, this array length macro would not work in the sum function because of array decay. So here I'm just printing the result which should be 15 if we implement this correctly. Boom. Cool. So we got our answer recursively. We could also go ahead and just add the last element to the back, means the same thing. I could also change my base case if I want so I could just say maybe I wanna cover the case where the array length is zero. Maybe I just make my array length, make my base case the array length of zero and just return zero. So in case you pass an empty array, maybe we get a proper solution for that but same thing, I'm just going to get 15. All right, cool. Any questions about that? Alrighty, so we are experts now. So again, here you have the solution in your slides means the same thing. So you could think of another solution here. Let's go ahead and think of another solution. So you might be like, oh, okay, well, I should just go ahead and maybe I just add another argument to here that just says, I don't know, the current sum. So add another argument that has a current sum and then my base case could be if the array is empty, well, then I should just return the current sum and the idea is going to be, well, I keep on just keeping track of this current sum. So I could change my recursive step instead of being this. We have the same idea where we sum the smaller version of the array by one but we would just take the current sum and then we would add the current index to it instead of just returning the value directly. We'll just use a function argument for that. So this will also work. The only difference is, well, I should just give my current sum as zero whenever I am using it but other than that, if I go ahead and compile it, this will work just fine. So another solution if you wanted, technically this is like, if we remember the tail recursion thing, we don't have to remember, this is like the tail recursive version of it. So you might see a function like this and be like, well, why does it have an extra argument? That is real ugly to use. I don't want to have to remember to put a zero on it. Like I just want to sum an array. I don't really care about this extra value. So if you happen to think of this as a solution, it is all right but what typically you do is you just could rename this as like a helper function. So generally, if it's a function that you're using for recursion, that you don't want someone to actually use, you might call it a helper function, just something us weird programmers do. So we instead rename it helper function and then we could instead define sum just using those two arguments so that it's a bit nicer to use. So I don't have to remember to use the third argument and for sum, its implementation could just be actually just using the helper function, passing in the array and the array length and then just setting the number by itself so that when I go to use the function in main, I don't have to remember to go ahead and always tack on a zero. So now I compile and run that. I get 15 again. Again, means the same thing. Just if you do happen to think of an easier solution that just needs more function arguments, typical thing to do, write a helper function, go ahead and just have a function with a better, less arguments and have it call that helper function for it, for you. So any questions about that before we get to actually figure out how mean parents are to their babies, apparently. That will make sense in a second. So, let's see. So, okay, so now the thing that will make sense. So, there is this. I don't know if you can see it. So wooden toys accompany your baby to grow up. So this is the Tower of Hanoi. It says, let's see, who are we got? We got 36 months plus. So I hope everyone is of age for this. So hopefully we can solve this. So has anyone ever seen this before? The Towers of Hanoi. So we have one, two people. All right, so I'll do a simpler version of it. So the rule is, all right, I have a green screen effect on, apparently. Oh, right, I do. Can I turn that off? Filters, look, you can, filters. Hey, there we go. All right, so green screen effect removed. All right, so the rule of this game is they are three, what do they call them? There are three, let's call them rods. And our goal is we want to move all four of these discs from this rod to this rod. And the rules are as follows. I can only move one of these discs at a time. So I can only move one disc at a time from rod to rod. And the other rule is that, see how they are different sizes. So it goes from big to small. The other rule is I'm not allowed to place a big disc on top of a small disc. So I'm not allowed to do something like this. This is invalid, we are not allowed to do that. So the only valid move I would have is to do something like this. So using those rules, we need to get all of these discs from this over to here, following those rules. So let's make it a bit easy first. So if I just have two discs, what's my first move? So I have to move this, let's see, should I move it here or here? All right, one, two, three. So I'll call them one, two, three. So we got twos, all right. So drop this here, allowed to do that. All right, so now with this disc, I can't do that, but I can do this. So that's fine. And now I can take this disc, move it here. Oh wait, that didn't do anything. All right, this is why they give it to the baby. All right, so I can move it there, boom. So that's solved. So how many steps did that take us? So in order to move all these discs from this peg to this one, it was one, two, three. Not too bad, all right. So let's do hard mode. So now we got three. All right, should I move the green one to disc two or three? So A votes for two, votes for three. All right, everyone's smarter than I am. All right, so I do this, okay? So now I only have one move, right? I can own, well, I could move the green one back, but that wouldn't really make sense. So I can only move this one. I can't move it on top of the green one because it is bigger, so I have to move it here. So now what should I move? The green one, where do I move the green one? Here? Okay, the green one goes here. All right, so green one goes there and now the blue one can move to where it should go. Okay, and now the green one, oh no, okay. Green one moves here, then we go here, then we go here. All right, so how many steps was that here? It was easy solve. All right, so that was what? One, two, three, four, five, six, seven. Four, five, six, seven. Okay, so how do I solve this with four deaths now? Yes, this will be on your final. And remember, so yeah, you only have to be, what's that, three? Why is it in months? Why doesn't it just say three plus? I don't know. It makes you feel older if it's in months. All right, so this, this, where do I want to move it? Let's see, votes for two? Okay, three? Okay, no, all right, so I move it here. Okay, here, yeah, here, here. Oh, you got bamboozled. So I'm not doing this. So this is too hard. So did anyone think of how to solve this recursively? So did anyone catch it while we were solving it when it was easier? So maybe here, let's do four again. So see if you can spot any recursive steps here. So what did I do? Make sure, anyone spot anything recursive? So was there at any point I solved this problem? So this is essentially, the problem is I have four disks. I want to move them from rod one to rod three using rod two as just kind of a spare rod that's chilling around. So is there a way to solve this problem in terms of three disks? So this could be my smaller problem, right? Just having three disks. So anyone want to tell me how I solve this problem in terms of solving these three? So you're thinking of just in terms of smallest disk what's happening to that, right? They kind of follow, but we want to think it in terms of like solving it with a smaller problem in the case these three, yeah. Yeah, in terms of smaller problems like a recursive problem. So I want to move these four disks from rod one to rod three. I can use rod two as a spare. Well, my recursive solution could be, well, if I put it in terms of a smaller problem, well, I really just want to move these three disks. So this is my smaller problem. I want to move them to rod two using rod three as a spare. So I could use that whatever I want. And now, well, this is my one step I have to make. I just move the big ass disk to here. The big ass disk, it's the technical term. Again, it'll be on your exam. So then after that, my other thing I need to do is I have to move these three disks. So again, another smaller problem from rod two to rod three using rod one as a spare. So I do that, right? So that works even for this. So even if I have eight, so eight, that is eight. So even if I have eight, well, I could move seven to disk, or to rod two, then move the big one to rod three. Then I would take these ones, all seven of them, and move them to rod three. And then I would be done, right? So let's write a recursive program for that. Because it turns out doing this recursively is a lot easier. So let me go ahead and switch. All right, we will show these babies what for. So I'm going to define a function. So I'll give it four parameters here. So an int of the number of disks I want to move. And then I'm just going to give an integer number to the from rod, and then one to the to rod. So I'm moving from, in this case, how I want to use it is to solve a problem of moving any number of disks. I'm moving them from rod one to rod three, like I numbered them in the example, and then I'm using rod two as my spare rod. And then with this function, I want to return the number of steps I need to take. So I'll give you a hint for our base case. We're going to print the number of moves and just go ahead and keep track of them for my base case. While my base case is if I have no disks to move, then I don't have to do anything, right? I can just return zero. I'm not moving anything. Don't have to do anything. Don't really have to worry about anything past that. So what was my recursive step? So I want a new variable called steps. So let's go back. So how would I explain my first step? So my step should be, let's see. So if I have n disks on rod one, I want to take n minus one disks of these. And then if my goal is to get this whole stack from rod one to rod three, well, I need to take my big stack, which is n minus one, and move them from rod one to rod two, right? So if I just go ahead and try to program that, well, that should be Hanoi disk minus one. So that's my current disk, my from rod. So initially it's rod one, two, and three. So I'm just giving them numbers to be more generic. So they start on the from rod, but I want to move them to the spare rod, right? So their destination, which is the second number or second rod in this function argument, basically this argument, I want to move them to the spare rod and then I'm using whatever was the two rod as the current spare rod, right? So that's one part of the problem. And then my other step is, let's see. So that should solve this part. Now I need to express just moving the big disk from the from rod to the two rod. So I can just say, move the disk to rod to rod and then that also counts as an additional step, right? So I just add one to the steps because I just moved the big one. Then the other part of the problem is the last step. So the last step is to move this current stack, which is N minus one disk from the current spare rod because that's where they are all the way over to the two rod like that. And then I can use this one as a spare. So if I go back to it, I would do steps plus equal Hanoi disk minus one. So currently they're on the spare rod and then I want to move them to the two rod and then I can use the from rod as the spare rod and then just return the number of steps. So that, believe it or not, that's the whole solution. So now if we go ahead and build this, assuming I haven't screwed it up. So you could see solving the Hanoi with one disk. So it just says move disk one to rod three. That's the same as we got, right? We just moved the disk to rod three. We solved it in one step. What about two steps? Well, it says we moved disk one. So I ordered the disk in terms of, like disk one is the smallest then disk two is slightly larger than that, then disk three. So this says move disk one to rod two, move disk two to rod three, move disk one to rod three and then we're done. Now we can give it with two, three, make sure that it matches so it was seven steps like we did before, it seems to work. Let's, what's my time? Oh yeah, we got time. All right, so here is with four disks. So this is about where I maxed out and my intellectual capacity got exceeded here. So let's see if this one works. So you can still kind of see it. So first step it says this is disk one, two, three and four. So it says move disk one to rod two, move disk two to rod three, move disk one to rod three. So that's right here. And it says move disk three to rod two, move disk one to rod one, this is rod one, sorry, move disk two to rod two, move disk one to rod two like this. And see, this is the first half of our recursive solution. If we're just taking into account disk four, then it says disk four moves to rod three. And then we just do the same steps over again. Well, not the same steps. We do the other sub-problems. So we move disk one to rod three, disk two to rod one, disk one to rod one. And then disk three to rod three, disk one to rod two, disk two to rod three. And then the final step should be disk one to rod three. And it's 15 steps and we have a solution for it, right? So now to get off why I said this is mean to babies. So remember, the current thing is this, eight, right? How many steps do you think this takes? 2,000? So well, good thing we wrote a program to do it for us, right? So I don't have to worry about moving disks around for like an hour. So let's go ahead and see. So was that eight this? So let's see all the instructions that will give us. 255. So now you can see why this was designed for babies because if the baby is really, really smart, still takes some 255 steps to do it, which should keep them occupied for like a good hour or two, right? So this is why this is a good child's toy because provably it just wastes their time. So, and it gets worse. If your child's really, really smart, this scales up, right? I just make my own little disk and then give them nine. And then suddenly it takes 511. Okay, well, smart baby, all right, give them 10 disks. Oh, that'll keep them busy. And then if you really want to keep your busy baby or your baby busy for a lifetime, let's give them 20, probably shouldn't have done that. So that should keep your baby busy for pretty much a lifetime. And yeah, it's basically to the power of N minus one. So any questions about that? So now we can prove that this toy is kind of cruel to babies because, well, I mean, come on, am I really gonna do 255 steps to do that? How long would that take me? I could follow all the steps that are there, but yeah, it takes a while. Cool, though, right? All right, any other questions? Cool, so practical use of recursion, finally, because I give you a challenge. Try and write that with a loop. It will be, you can do it, but to think of the solution, you probably had to think of the recursive solution anyways, but still try and do it. Turns out to be fairly tricky, but we'll have time, so just remember, pulling for you, we're all in this together.