 Okay, we'll run these quizzes. So here is the first question. A function that calls itself is an example of what kind of function. The four choices, recursive, iterative, non-terminating and mirror should be able to write down this answer in five seconds. The next recap is which of the following is or which of the following are false about recursive functions? The four choices given, any one or more may be false. So please read them carefully. Choice A is, must have at least one parameter. Choice B can have only called by value parameters. Choice C may not terminate for some input parameters and choice D cannot call any function other than itself. So whatever is true about a recursive function, you will leave that aside. Whatever is false, this is the third one. For any recursion to terminate the values of parameters, A can change in any order. B must move monotonically towards a termination case. C must stay unchanged in all calls to the function and D must never become negative. So which one of these is the right answer for the values of parameters to be modified such that the recursion terminates? Viranka numbers, which were mentioned in one of the lectures, can be computed and these are four choices. Can be computed recursively but not iteratively. Can be computed iteratively but not recursively. Can be computed both recursively as well as iteratively and neither iteratively nor recursive. Again a straight forward question, next question. When a function recursively calls itself, which of the following happens? A, the activation record on top of the call stack is popped out. B, a new activation record is pushed in the call stack. C, the activation record on top of the call stack is overwritten. Or D, no activation records are pushed or popped. These are the four choices. So when a function recursively calls itself, what happens? Again a fairly straight forward question. Alright, we move to the next question. Specifying a termination case. A, guarantees that a recursive function terminates for all inputs. B, may not cause a recursive function to terminate for all inputs. C, terminates a function if it calls itself and D, helps the compiler avoid generating code for recursive function. So let's spend two or three minutes quickly discussing these questions. Question one, a function that calls itself is an example of what? Recursive, so the answer is A. Which of the following are false about recursive functions? A must have at least one parameter, not necessary. How will recursion work? It can read a variable and then call itself recursively and return a value. B can have only call by value parameters. It can also have call by reference parameters. C may not terminate for some input parameters. Possible if it is not well formed. D cannot call any function other than itself. So you say only C is true, I mean C is false or what are all false? B and D. A and D. Okay. I would like during the discussion session for people having these different answers to discuss it and figure out what it is. For recursion to terminate, which of the following options is correct? Must have monotonically decreasing or moving towards a termination case? Is that clear? Why it must be so otherwise the recursion will not terminate? What is the answer to this question? See you can of course compute this both iteratively and recursively. In fact we will look at some of these aspects through an example later. When a function recursively calls itself, what happens? A new activation record is pushed in the call stack. When a function is called from any program, even if it is not recursive, wouldn't there be an activation record that get pushed? So in short, this is the process of invoking a function. Whether you are invoking it from within a function recursively or whether you are invoking a function only once, an activation record has to go on to the stack. What is the answer to this question? May not cause a recursive function to terminate for all inputs. Depends upon how carefully you have chosen the input value and how carefully you are writing your function. So we have seen that calling a function from itself has same mechanisms of function calls and returns which we have studied. That means an activation record gets created when a function is called. Whenever a function returns, that particular activation record from the stack is popped out and the values are appropriately returned, values are transmitted and so on. So there is nothing new in the mechanism except that when a function recursively calls itself upon successive returns, the then existing values of the internal variables are used for the subsequent processing after the recursive call. It's an elegant and natural way of solving many problems while iteration is contrived. I will mention that in a short while. What about mutually recursive functions? Function 1 calls function 2 which calls function 3 and which calls function 1. Perfectly possible but that's a mutual recursion where recursion is not so very obvious. What is important is that the same mechanism of function calls and returns which works perfectly for this. So what does it do? It actually takes the integer question, Q1 marks, integer Q2 marks. This is the statement which invokes that function from the main program. Cypher equal to new n, Q1 marks comma Q2 marks. So this new encoding function, this is the function. It has two integer parameters. Now look at the switch. Switch is on Q2 marks. In one case when Q1 marks are equal to 1, it returns 6, else it returns 2 into a recursive call Q1 marks minus 1. So what does it do? Q1 marks are successively reduced for whatever value of Q2 marks that you have given. In case it is 1, it will do this, else it will return this value. There is a break after this. What does the default case mean? Anyway, we have discussed this in the case. So what does it do basically? Let us just discuss this particular part. So what will happen if Q1 marks is not equal to 1? For example, let's say it is 3. It will take these marks 3. It will multiply 2 by these into a function called Q1 marks minus 1. That is 2. Since 2 is still not 1, it will come back again and do it. It will multiply 2 into this of 1. And then last time when it will come, it will find it is 1. It will return 6. Will the final value return be 6? No, because this value will be returned to the previous call. And that will be used to calculate something new which will be returned to the previous to previous call. And finally, when you come back through all these jambela, you should get the right answers. So here are some caveats or rules. Number one, you must specify how to terminate the recursion. Otherwise, it can go on forever. And it is as hard to spot an endless recursion as it is to spot an endless iteration. Both require application of mind while you construct the program. This is more important for the recursive function. You must ensure that in any invocation, the recursion changes parameters in an orderly fashion and how that the recursion eventually terminates. So that is the responsibility of the person who writes that function. So look at this. Encode m into n. It encodes m comma n minus 1 into 3 if both of them are greater than 1. Encodes m minus 1 by 1 into 2 if m is greater than 1 but n is 1. And it returns 2 into 3 equal to 6 if m is equal to 1, n is equal to 1. This is the termination case. But this is the termination case for the innermost invocation of the recursive function. The innermost invocation by which time these values are reduced to 1 and 1 are still like this. And when you compile all other computations that would be done with this, further returning some values, etc. Notice that the parameters are changed in an orderly way to ensure termination. So when you start with encode m comma n, you actually invoke encode m into n minus 1 if m is not greater than 1. Only when this n reduces to 1 that is you have a larger value of m but n is equal to 1 then it suddenly causes encode m minus 1 comma 1 into 2 again for successive values of this. Each one ensures that the target of the parameters are moving towards a value where you will have the termination case raised where m and n are one. So think of all possible valuations of parameters as ordered with a fixed n. And recursion must change values of parameters so that we move along this order monotonically towards the fixed n. Of course there would be situations where this monotonic movement could take one of several alternative ways. For example, if you take this to be the grid, it shows plot of m and n for different values of m and n varying from 1, 2, 3, 4, 5, 6, 7 here for m and 1, 2, 3, 4, 5, 6, 7 here for n. What is the target destination of termination of the function? When both of them are one. So this is the termination case. Where you can start? You can start from any point on this grid depending upon the initial value of m and n. So this is where you have given encode 4 comma 3 for example. m is 4, n is 3. How do you think your recursive function should move? Will it move like this? The way it is coded, the way it is programmed, will the program move the values of m and n like this? Changing both values simultaneously? No. The first movement is downwards, where the value of n is reduced from 3 to 2 to 1. Once n becomes 1, then the value of m is successively reduced such that m becomes 1. This is how the way the program is written. Is it not possible to do it the other way around? That first the value of m changes and then the value of n changes depends upon the sequence in which you have written the code or the program, the statement in your program. The point is you cannot have a situation where this value threatens to go this side because if it does then there is no termination. So this is the care that you are required to take when you solve this problem. There is also an issue on recursion versus iteration. You have done iterative solutions, they are fairly straightforward, they appear but many times you require a whole lot of thinking to convert a particular algorithm which is actually seen to be a recursive algorithm by the very nature of its specification and you are converting it into an iterative solution. The difference between the two is as follows. If the underlying algorithm, underlying formulation is recursive then a recursive formulation of your program is more intuitive, is natural and succinct, very precise but you need to worry about the termination of the recursive. And of course you need to worry about the number of recursive calls. I will explain it through an example why these could become excessive and therefore may cause a lot more computations to be done instead of what could be legitimately achieved with fewer computations. Iterative formulations on the other hand may be sometimes not very clean, not a solution and you need to worry about the loop invariance now. Just as you have to worry about recursion and recursive functions, you have to worry about iterations terminate. You have to define the loop invariance and the termination criteria and these could be very efficient if done proper. The efficiency point we will discuss in a moment. The best practice is to use a judicious mix of iteration and recursion. Before we go to the next problem I wanted to tell you about a problem which actually is a simple problem. You have written solutions for that problem as a part of writing down iterative programs but the problem itself is fundamentally stated in terms of a recurrence relation. So let us look at that problem. This is a recursive formula. What does it represent? Factorial of n. All of you have calculated factorial many times. How do you calculate the factorial? Suppose you have int nf which is the final result and let us say variable i. I will read the value of n. I am not writing all validations etc but we presume that n is a positive integer. So it is a positive non-zero integer to be very precise because this formulation presumes that f of zero is equal to what is factorial zero. So this is the starting point to kickstart. If I have done this then I can simply start with f equal to 1. Set up a simple counting iteration. This iteration will be executed how many times? n times. i equal to 1, 2, 3, 4, 5, 6, 7, 8, 9. And if I simply say f is equal to f multiplied by i and come out here and I output f. Will I get the solution correctly? I will calculate factorial n assuming of course that the input is a positive input. You are all familiar with this program. It is easily done but this program implements the solution using an iterative process. If you were to do it recursively how will you implement it? So let us say instead of this portion I want to have a statement which says f is equal to... So I do want to calculate f. I read the value of n here. I invoke a function f is equal to fact n and whatever I get here I print that out. I want you to write a recursive function fact which takes n as a parameter and recursively calls itself to calculate factorial n. In short a recursive function which implements this particular formula which is a recursive formula. So this is done by Rajat just defines in factorial n. If n is equal equal 0 returns 1 else return n into fact n minus 1. The only problem is the star is written slightly above the line and there is no declaration of n either within the program or within the parameter. Here is another one by Prandtl. In fact in 10 if n is equal to 0 it returns 1 else it returns n into fact n minus 1. In short solution I had written it the same way except that I try to write it more formally. This is my own personal preference whenever I write a form whenever I write a file. In short whenever I write a construct which permits more than one statement to be written then I always write brushes. Suppose tomorrow I want to modify this program and add one more statement here and I forget to add brushes it could have disastrous consequence because then this statement would not remain part of that F. Else I return fact n minus 1. So very simple it will recursively call itself and when it comes down when it reduces n to 0 it will return 1. What is the termination condition? n is 0. How are we guaranteeing that the termination will be reached? Because the recursive call to fact always reduces n by 1 wherever it is. Now let me ask you another question. You have studied Virahanka numbers. What is the recursive relationship which defines the nth Virahanka number? Is that correct? This is a recursive formulation which you can easily use using a recursive call just like you return n into factorial n minus 1. Now which formulation is better and what a recursive formulation in this case is very natural and intuitive but look at the amount of work that the computer has to do. Consider both cases. First consider this function. Every time I come here the compiler will have to create an activation record and put it on the stack. When it then goes back again to do these computations it will again do this check. If it is not the compiler will call the recursively this function again another activation record. So there are overheads. If n is equal to 5 the recursive call will be 5 times and there will be 5 activation records created. As against that if you see the iterative solution there is lot of work there also. The iterative solution has to do how much work? First 5 multiplications i into fact successive. But more importantly it has to 5 times increments the value of i and 5 times check whether i has become equal to greater than n. It is greater than n I have to quit. So 5 extra comparisons and 5 extra increments that will have to be done other than doing 5 multiplications if n is equal to 5 and you are finding factorial 5. As against that this will have to create activation records 5 times. Now creating an activation record pushing values of current variable there then deactivating when you come out and again assigning those values obviously takes slightly longer. So an iterative solution in this case will be slightly cheaper than a recursive call as far as the computations are concerned. Now consider this in this case you first call the function vn this will recursively call agreed because in the same statement you are saying vn is equal to vn minus 1 plus vn. In order to do this you have to call vn minus 1 and you also have to call vn minus 1. vn minus 1 when it is called this in turn will call vn minus 2 vn minus. This on the other hand will now call what? vn minus 2 will call what? Do you notice the extra work that you are forcing the compiler to do? vn minus 3 was called by vn minus 2. vn minus 2 is independently called in another chain of events and that in short invokes this. In fact it is not difficult to understand that there will be a large tree of functions which will be called successively like this and the entire tree will be repeated here and this will happen for every such call. So the number of function calls that get generated the number of activation records that get generated and the activities that need to be done in terms of computations comparisons assignments additions etc etc are far too costly. In fact try to run with a double or some such large it is not just an end it is a long end. Try to run the factorial calculation both iteratively and recursively even on code blocks or any other thing you can see the significant difference in the amount of computing time it takes. The computing time for this will be extraordinarily large. So what do we learn from this? Number one if the computations are to be performed are very tiny like an addition in this case or a multiplication in that case then the overheads which the recursive functions cause may not be a very good idea particularly when the number of calls are going to expand these are going to expand exponentially as you can. On the other hand if the amount of calculations to be done is significant then there is some elegance and some simplicity in programming using recursive function and then it is worthwhile to pay the overhead cost of recursive function. So that is why the last line is very important best practice is a judicious mix of iteration and recursion. Choose iteration when this is appropriate choose recursion when the other one is appropriate but in either case be very sure to write your programs carefully such that either iterations or recursions terminate properly. Some more practice problems which indicate the need for being careful on writing such terminal conditions. So this practice problem builds on the problem that we have discussed very briefly in the class the game of tic-tac-toe. You remember the game of tic-tac-toe? One fellow puts a clause here another fellow puts a tick here and so on and we have said that we will use zeros and ones to mark the play and we have said that instead of such empty squares we will put the value minus one to denote something which has not been written into yet. So this is called the configuration of our tic-tac-toe box. Somebody can put a zero here somebody can put a one here some first player puts a zero here second player puts a one here third player idiotically puts a zero here and the second player wins. This is very clear fine. So this is the winning move. So what is the winning move? Any move in which you have three ones or three zeros in succession across a line column or diagonal. So configuration now this is a problem this is a programming problem. A configuration of the tic-tac-toe grid is represented by sequence of nine integer value. These values are stored in variables x1, x2, x3, x4, x5, x6, x7, x8, x9. These values represent the following points of the grid. So this first row is represented by x1, x2, x3 second by x4, x5, x6. The third column is represented by x3, x6, x9. One diagonal is represented by x3, x5, x7 etc. Once you know the thing then in whichever way x1, x2, x3, x5, x9 etc return you know exactly which variable represents which position. So this is the configuration. What should be the possible values of x1, x2, x3 etc? Zero, one or minus one. There is no sanctity of any one of these values. We are presuming that minus one represents an empty square. We are presuming that zero is the symbol used by the first player and one is the symbol used by the second player. So this configuration that we had seen will actually translate into this. x1 will be zero, x2 will be one, x3 will be zero. Here x4 where there is nothing is minus one, x5 is one and so on. So if these are the values of variables then this is the photographic image that it should represent in our mind. This is the situation, the board. There are a very large number of possible combinations because there are nine variables and although each one of them can have only three values, a large number of possible value. But not all possible combinations are correct configurations. For example, all variables having value zero, is that a possible correct combination? It's like I'm playing with no opponent there, I make my first move, then I make my second move, then I make my third move, I win but I still make my fourth move, fifth move, etc. And I put all zeroes. That is not a valid tic-tac to combination. So amongst the very large number of combinations there are some which are valid, some which are not valid configurations. You have to write a C plus plus function that takes as an input and input configuration and determines who should move next. The simple problem, please note, no iteration, no recursion, nothing, just a function. The function name is next turn and the next turn should hopefully return which of the two possible values? We have already assumed that the first player marks zero, second player marks one. So if next turn returns zero, it is the first player's next move. If the next turn returns one, it is the second player's move. That's the assumption we'll make. You want this function to return a numerical value zero or one depending upon what you conclude by examining values of x1, x2, x3, etc. up to x9. First you have to check whether the configuration is valid. If so, count the number of zeros and ones and determine who moves next. That you have to return. In this particular case, who is supposed to move next? One, very quickly found out. What did you do? Counted all number of zeros, counted all number of ones. If they were equal, it was zero's turn to move. If they were unequal, one's turn to move provided they were unequal only by one. Otherwise the configuration itself is invalid. Remember what I told you? It's like one player saying zero, zero, zero before giving chance to someone. Not permit. Another player saying one, one, one. Not permit. Of course if you presume that the fellow who wrote the program and calling this function to get the next turn has done some sensible things there so that any extraordinarily bad configuration does not come to me to begin with. But it is still useful if the check whether the configuration is valid. So please write this function. Again repeat, this is a simple C++ programming exercise. Nine variables are given. x1, x2, x3, x4, x5, x6, x7, x8, x9. All integer variables. The values that you get in, you can assume that the values are guaranteed to be either zero or one or minus one. But you still need to check whether the configuration is valid. And if it is valid, count the number of zeroes and ones and determine who moves next. The first line of the function is given here in next turn, except what Professor Supratik has written as dot dot dot is not permitted. So you have to fill up your notebook by saying int x1, int x2, comma int x3, comma int x4, comma int x6, comma int x7, comma int x8 and comma int x9. Right? And then bracket close. Then of course you have to put a brass here and start writing your program. No dot dot dot. Because the morning when I showed this, some fellow wrote a code to look at x1, then look at x2 and then wrote dot dot dots. The C++ compiler does not understand dot dot dot. So it has to be a properly written function. So is the function defined as in next turn is defining i and j. As you can guess, he is putting the count of all zeros in variable i and all ones in variable j. Starts with the count as zeros and he meticulously examines each variable. If x1 is 0, i is incremented, else if x1 is 1, then j is incremented. For any other value set neither of the counts is incremented. He evaluates these for each of the 9 variables x1, x2, x3, x4, x5, x6, x7, x8, x9. Now he examines the total number of zeros and total number of ones. If the total number of zeros are equal to total number of ones, then the next turn is that of the first player that is 0, so he returns 0. If on the other hand, i minus j is equal to 1, which means the number of zeros is more than number of ones, then that is the turn of one, so he returns 1. If the number of zeros or number of ones are more than number of zeros, is that possible? The second player could not have played more number of times than the first player, so that is invalid. Any other combination is invalid, so he returns minus 1. In fact I would think even this condition is not required. It is actually a simpler solution if you count only the number of minus ones, that is you assume that the numbers are either zeros, ones or minus ones. Then the next turn can be obtained only from the count of minus ones, how? Depending upon whether they are all or even. However, it presupposes that the numbers of one and numbers of zero are in proper fashion, that means they either same or they differ only by one, otherwise somebody has goofed up in sending the configurates. Here is another solution where a wide count function is defined. It has a variable, it is a variable c0 and variable c1 and these are passed by reference. So that means whatever are the original c0 and c1 variables, they will be affected if they are changed here. The variable x is one of the nine, when you come here that variable is examined if it is zero, c0 is incremented, else c1 is incremented if it is one. If it is neither, none of the counts are incremented. So notice how the author has cleverly avoided writing very long statements in the program. All that the author does is count of x1, count of x2, count of x3, count of x4, these are all function invocations. So the same function count of x is being invoked. There is no assignment, no nothing, no value return is expected because the calculations of the counts are being done inside the function using variables which are passed by reference and which are defined, these are the definitions, these are the main functions. So in this function int c0 and c1 are defined as integer variables. The poor fellow started off by examining its individual variable but I want each one of you to appreciate what the program is doing. Generally whenever we talk of functions recursive or otherwise, we have always seen the function references coming in some assignment statement, y equal to function of something, y equal to n into function of something. We have ordinarily not yet encountered in a program that we write a function which is just a standalone invocation like this here. Please remember that when I invoke a function like this, count x1, the value returned is not going to be used anywhere. So there is no point in having a function which returns a value if it is going to be used just as a standalone call. The objective of this function is somehow to go here with a different variable each time, once with x1, once with x2, once with x3 and so on. And all the if statements that you are ordinarily required to write for each of these variables independently are now cleverly written inside this single function here. So if the variable that you get is 0, you increment the 0 count. If the variable that you get is 1, you increment the 1 count. And after these 9 invocations, all your decision is going to be based only on c0 and c1. I have not examined whether this invocation is right or wrong. Please look at this and comment whether this will work. If c0 is equal, equal c1 plus 1, player 2's turn. c1 is the count of 1's, c0 is the count of 0's. Number of 0's is 1 more than number of 1's. So clearly second player's turn and return 1. Else c out player 1's turn, return 0. Of course, Nudul is expecting that the fellow who is calling this function with these 9 variables is not behaving idiotically and is sending me a configuration which has only 0's, 1's and minus 1's and nothing else. That's the assumption, right? So basically it's a valid thing. But if these two conditions are not correct, the returning minus 1, because any other combination where the number of 0's and number of 1's mismatch by more than 1 is invalid. This is a good solution here. So this was just another way of doing things. Understood? Alright, let's go to the next solution. Where instead of if and else's, the person is simply counting all 0's independently and all 1's independent. The only difference between this and a solution with else is that in the solution with if and else, the else condition will not be examined if the if condition is true. So the number of comparisons that you will do by using an else clause are going to be totally less because each number is either 0 or 1. If it is 0, you just calculate it. If it is 1, you may fall into the else clause. Whereas here you may do everything. For example, in a case where both numbers are equal, you may actually end up doing extra comparisons but the program will work correctly anyway. Alright, go to the next problem. So look at this problem. Given a configuration of tic-tac-toe, we want to determine if there is a winning or losing move of the next player. So what is the winning move of 0? A move of 0 from which there is at least one way for 0 to win, no matter how one plays. No matter how one plays, there is going to be a win for 0, at least one way. There could be more than one way and winning move of 1 is similarly defined. Here is an example. So this is a configuration. All the remaining ones are minus 1. So this is 0, 1, 0, 1. Now it is the turn of 0. So please note the winning move does not mean that 0 or 1 wins immediately. A winning move means that a move by 0 which if played, then no matter what one plays next, 0 will always win. Or the same way for 1 as well. So look at this. If the next move is that of 0 and he plays here, then the winning move of 0 is x7 equal to 0. Is this the only winning move? If instead of this, if I move here, is it not also a winning move if I put a 0 there? Because the second player can either block this diagonal or can block this diagonal but not both. Now there are two winning moves. The question that we have to answer is a move of 0 from which there is at least one way for 0 to win, no matter how one plays. So this is good enough. This move of 0 here is good enough. One can win like this or one can win like this. One meaning 0. 0 can win like this or like this. No matter what one plays. So guaranteed win for 0 no matter how one plays. Now this is the next stage in the game. You have already written a function which tells you back as to whether the configuration is correct and whose move is next. Now suppose you have 0th move. So you have to determine whether there is a winning move for you or if you are 1 you have to determine whether there is a winning move for 1 for you. So how will you determine that? Instead of this example, I am 0 I played this, 1 played this. I am 0 I played this, 1 played this. Now in this case do I have a winning move? Why I do not have a winning move? Because if I play anywhere here, here, here, here or here do I guarantee that whatever one plays I will win? No. In fact if I am stupid enough to play somewhere here the next player will play one here and I will lose immediately. So not only there is no winning move but there are a set of losing moves for me. So in this particular case next move of the 0 there is no winning move because cannot guarantee win for 0 for any next move. However I can find a losing move for 0. A losing move from which there is at least one winning move for the opponent. Thank you.