 Hello and welcome back. This is the second part of our two part lecture on recursive functions. Here is a quick recap of some of the relevant topics we have already studied. We have seen the use of simple functions in programs, the contract centric view of programming with functions, how control flows when functions are called and when they return, the use of activation records and call stacks, parameter passing both by value and by reference and we have also seen recursive functions in the last lecture. In this lecture, we are going to see how when you are trying to design recursive functions there are certain issues like termination and recursive changing of parameters that you should be careful about and we are also going to see a very brief comparison of implementing a solution via recursion or via iteration. Some examples in this lecture are from the book An Introduction to Programming through C++ by Aviram Giranade published by McGraw Hill Education in the year 2014. All such examples will be indicated in slides with the citation AGR book. Let us go back to the encoding example that we have been studying in the last few lectures. We want to sort quiz 1 and quiz 2 marks of CS 101 students but in an encoded form. The encoding is given by encode of m, n is 2 raised to m times 3 raised to n. All marks are assumed to be integers in the range 1 through 10 and in the last lecture on recursive functions we have seen that we can basically think of this encode function specified recursively in this manner where in order to compute encode m, n I could compute encode m, n minus 1 if both m and n were greater than 1 and multiply the result by 3. If n is equal to 1 I could compute encode m minus 1, 1 and multiply the result by 2 if m was greater than 1 and if both m and n are equal to 1 I can get result directly as 2 times 3 that is 6. We have also seen that the previous view of the encode function naturally lends itself to a recursive solution using C++ and here in this program this is the new encoding function which recursively calls itself using the same recursive formulation that we just saw. Now given this recursive solution one might ask that are we really sure that every invocation of new n that satisfies the precondition. Here our precondition is that both q 1 marks and q 2 marks lie between 1 and 10 but even if q 1 marks and q 2 marks always lie between 1 and 10 are we really sure that every invocation of new n is eventually going to terminate. Since new n is calling itself from within it therefore could it be the case that one invocation of new n calls another invocation of new n which in turn calls another invocation of new n and this process continues forever without the program ever terminating. So when we deal with recursive programs, recursive functions we have to be able to answer such questions. So there are certain caveats that you must be aware of when you are using recursive functions. The first is you must specify how to terminate the recursion otherwise this business of calling a function from within itself can go on forever. The second very important fact is that when you invoke a recursive function and within that function when you call that same function again you must be changing the parameters. If you did not change the parameters then this will be an infinite recursion. But when you changing the parameters you have to be careful about how you are changing them. You must change them in an orderly way such that you are guaranteed that the recursion eventually terminates by reaching this termination condition. So for example if I looked at this formulation of the function n code this gives us the termination case when both m and n are 1 I do not need to recursively call n code again I can terminate by returning the value 6 and this other part tells you how when a recursive call is made to compute the function n code how the values of the parameters change. And in this case note that if both m and n are greater than 1 n is decremented so n will continue to decrement until it reaches 1 and when n becomes equal to 1 and m is still greater than 1 then m is decremented so m will continue to decrement until both m and n become 1 in which case you would have reached the termination case of the recursion. So it is very important to change the parameters in an orderly way to ensure termination. So in general what you have to do when you are trying to design a recursive function is that you have to think of all possible valuations of the parameters ordered in some manner with a fixed end and we are going to identify this end with the termination. Now when you call this recursive function with a specific value of parameters you are at some point along this ordering and the recursion must change the values of the parameters in such a way that you move along this ordering monotonically towards this fixed end where you know that the recursion is going to terminate. In the context of our example we had two parameters m and n which took values between 1 and 10. So I could represent the different combinations of values as points in the grid over here I have not shown all the values up to 10 over here for the sake of clarity. This grid point here constitutes the termination case when we are here the recursion terminates and let us say that we are trying to compute n code 4, 3 so we are at this point of the grid. So how do we go about computing it? Let us take a look at the function that we have already seen earlier if both m and n are greater than 1 I will decrement n and when n becomes equal to 1 I will decrement n. So that is what we are going to do. We are going to decrement n first and reach this point 4, 2 we are going to decrement n again reach 4, 1 then decrement m reach 3, 1 then decrement m reach 2, 1 and finally reach 1, 1 in which case we have terminated. So what you might have noticed is that we systematically came down in this direction and then we systematically moved in this direction to reach the termination case and it does not matter which grid point you start from if you just come down vertically until you reach the m axis and then you come horizontally until you reach the 1, 1 point you are always going to terminate. So in more abstract terms or in more mathematical terms what we are really saying is that the values of the parameters must have an ordering among them which should be a well founded ordering. If you do not know what well founded ordering is it is fine just remember that the values of the parameters must be ordered in a certain way and in this ordering there must be a least element. Given any invocation of the recursive function with a certain valuation of parameters we are at some point along this ordering and the recursive calls must monotonically change the values of these parameters such that we move along this ordering towards the least element and the least element the fixed end of the ordering should be the termination case. So this is what you got to ensure when you are designing a recursive function. For example the same encode function 2 raise to m times 3 raise to n can also be thought of specified as specified recursively in this manner. Here instead of decreasing n I am actually increasing n and therefore I have to reduce the value by dividing it by 3 similarly here I am increasing m and then dividing by 2. Well these are also recursive formulations and this is of course the termination case this is fine. However for these recursive formulations we have a problem given a value of m comma n here the value of n is going to continue to increase here the value of m is going to continue to increase and we will not reach the fixed end of the termination case. So here although there is a recursive formulation the parameters are being changed in a way that does not ensure termination. So this is a bad recursive design if you use this recursive function then you are not going to terminate in general. So this is pictorially depicting what would happen if you use that bad recursive formulation starting from a point you would either increase n or increase m and you would be moving away from this termination case. Now we have seen the encoding example solved through recursion here is another simple example of recursion being put to use given n we want to compute factorial n and I am sure all of you know what factorial n is. So here is a simple program which has a precondition saying n is greater than equal to 0 the post condition saying the return value is factorial n and in this program we have used recursion to compute the factorial function in a very simple intuitive way. So we say if n is 0 then we return 1 because factorial 0 is 1 this is the termination case otherwise if n is greater than 0 then we return n times factorial n minus 1 this is the definition of factorial n and we just use the definition in this recursive formulation. Note that given value of n I am decrementing it so that I am moving towards 0 and 0 is the termination case. So in this case the recursive calls are changing the parameters in such a way such that we monotonically move towards the termination case. Here is the third example which is taken from section 10.3 of the textbook that I mentioned at the beginning of this lecture. In this example we want to compute virahunker numbers the 0th virahunker number as well as the first virahunker number are both specified to be 1 and for all values of n greater than equal to 2 the nth virahunker number is obtained by adding the previous two virahunker numbers. These numbers are also popularly known as Fibonacci numbers although it is interesting to note that virahunker studied these numbers in the context of counting specific types of poetic meters way before Fibonacci did. So here is a simple recursive program that just uses this definition to compute virahunker numbers as a precondition we take n which is greater than equal to 0 the post condition is we must return the nth virahunker number and what do we do inside the function if n is 0 or 1 we just return 1 we know v0 and v1 are already 1 otherwise we simply compute the n minus 1th and the n minus 2th virahunker numbers add them up and return it. This is as simple as the definition itself well this sounds very nice and elegant we might also want to watch how many recursive calls are being made when I am trying to compute let us say virahunker 4 in order to compute virahunker 4 I will recursively call virahunker 3 and virahunker 2 in order to compute virahunker 3 I will recursively call virahunker 2 and virahunker 1 and again to compute virahunker 2 I will recursively call virahunker 1 and virahunker 0 and virahunker 1 and virahunker 0 are really the termination cases so I am not going to make any further recursive calls there. So what you notice in this tree like structure is that virahunker 2 was computed twice and so the entire effort of computing virahunker 2 here is going to be replicated over here this is one recursive call this is a completely different recursive call and so that entire effort is going to be replicated and it is a very interesting exercise to show that the number of calls required to compute virahunker n using the recursive function that we just showed grows exponentially within this is not good because this means that we cannot compute virahunker numbers for large values of n. So is there a better way to compute virahunker numbers it turns out that there is and this is an iterative solution so here I have shown the iterative solution basically in the termination cases we return one directly otherwise we have a loop which keeps iterating from two onwards because we already know virahunker 0 and virahunker 1 so this keeps iterating from two onwards until it reaches the input value n and in each iteration of the loop I compute the next virahunker number by adding the previous two virahunker numbers. So here prev vn is sort of the n minus 1th virahunker number prev prev vn is n minus 2th virahunker number and initially I initialize them to be both one so this is the first virahunker number and this is the 0th virahunker number then the second virahunker number is obtained by adding those two and within the loop I then update the values of prev prev vn and prev vn because when I am going to go around the loop and increment the value of the counter then whatever was prev vn earlier should now become prev prev vn and whatever is result now should become prev vn and then I can compute the next result so when this loop terminates I have the nth virahunker number and I can just simply return that so now that we have seen the same solution using recursion as well as using iteration we might want to ask which is a better one well the recursive formulation is usually clean intuitive and very succinct but you have to worry about recursion termination the well founded ordering of parameter values and you also need to worry about how many recursive calls are being made the iterative formulation may be less clean or intuitive although this is not all this may not always be the case but you need to worry about loop invariance loop variance and termination of the loop and if you formulate the iterative solution properly this can indeed be very efficient the best practice is really to use a judicious mix of iteration and recursion so in summary we looked at recursive functions we looked at their termination and the ordering of parameter values how it is very important to order the parameter values to ensure termination and how the recursive calls should monotonically move along this order towards the termination case and we also saw a very brief comparison of recursion versus iteration thank you.