Recursion Mathematically, a recursive function is one that is defined in terms of other values of that same function. A couple (overused) examples are as follows: Fibonacci numbers: F(0)=0, F(1)=1, F(n) = F(n-1)+F(n-2), for all n>1. (F is defined for all non-negative integers here.) Factorials: Fact(0) = 1, Fact(n)=n*Fact(n-1), for all n>0. (Fact is also defined for all non-negative integers.) Analogously, a recursive method is one where the method body contains a call to the method itself. (Remember, each instance a method is called, the computer allocates some memory for THAT instance to run. There is no reason why two DIFFERENT instances of the same method can not be in the middle of running at the same time.) You have seen recursion in CS1. Probably most of the recursion you saw in that class was straightforward, such as implementing the two functions listed above. In this class we will look at more complicated recursive algorithms. Incidentally, here are Java methods that compute the two recursive functions listed above: public static int Fib(int n) { if (n<2) return n; else return Fib(n-1)+Fib(n-2); } public static int Fact(int n) { if (n ==0) return 1; else return n*Fact(n-1); { What would make both of these methods more robust? What problem becomes more probable with recursion? When dealing with recursion, there are essentially two issues to tackle: 1) How to trace through recursive code. 2) How to write recursive code. Tracing through recursion Clearly, the first is easier than the second, and you can't do the second if you can't do the first. So, let's talk about tracing through recursion first. Really, there are no NEW rules to use when tracing through recursion, as compared to tracing through code that has method calls. Anytime a new method is called, you checkpoint and STOP running the calling method, and start executing the callee method. When the callee has terminated, it returns a value to the caller, and then the caller continues execution EXACTLY where it left off. The stack trace illustration your CS1 book uses is a fairly good model to use when tracing recursion. I often explain a trace using a physical stack of papers, where each paper is allowed to keep track of one method call. Let's trace through a couple simple examples of the two functions above. Writing recursion The toughest part with writing a recusive method ISN'T coding the method. Rather, the most difficult part is coming up with the recursive algorithm to solve the problem. Once you come up with the idea for the algorithm, the actual code is not so difficult to write. (As you can see, the mathematical definition of both fibonacci and factorial look amazingly similar to the code. Once you can get this mathematical definition, the code is not far off.) The most difficult part of creating a recursive algorithm is finding a way to solve the given problem that involves a solution to a problem of the exact same nature. In both the Fibonacci and Factorial examples, the functions themeselves are defined recursively, so we don't have that problem. Here is a problem you were probably shown in CS1 that is not typically defined recursively: Find the sum 1+2+3+...+n. You probably did a for loop in a function to find this sum in CS1. Then, in order to come up with a recursive solution, you first had to think of a mathematical way to express the function above, recursively. The big key is that 1+2+3+... = n + (1+2+3...+n-1) Thus, if we let s(n)=1+2+3+...+n, then we can derive that s(n) = n + s(n-1), for all n>1, s(1)=1. Now using the information above, the recursive method that computes the function s above is apparent. Once you have found a recursive algorithm to solve a problem, there is one other main issue to consider before coding a recursive method: For what values should the recursive algorithm be executed, and for what values should it NOT be executed, because the value to return can be easily computed? This is known as the terminating condition. All recursive methods have to have a terminating condition in some shape or form. If they didn't, you would get infinite recursion. (This is very similar to an infinite loop.) There are two basic constructs that most recursive methods take. Here they are: if (terminating condition) finish task else solve problem recursively if (not terminating condition) solve problem recursively One other way to view coding a recursive problem is the following: Imagine that you have been asked to write a method, but that someone else has already written the method and you are allowed to make as many calls to THAT method that you want to, as long as you don't pass it the same parameters that have been passed to you. Thus, when computing Fact(n), for example, instead of doing all that multiplying yourself, you call the function your friend has written to calculate Fact(n-1). When he/she returns that answer to you, all you have to do to finish your job is multiply that answer by n and you are done!!! Recursive Binary Search Here the key is that we have two terminating conditions: having no search space, and if the middle element is the target. After that, based on our comparison with the middle element, we can make one of two recursive calls for a search in either the lower or upper half of the array. public static boolean search(int [] vals, int low, int high, int t) { if (low > high) return false; int mid = (low+high)/2; if (vals[mid] == t) return true; else if (vals[mid] < t) return search(vals, mid+1, high, t); else return search(vals, low, mid-1, t); } Minesweeper - Recursive Clear Minesweeper is the popular game included with windows where the player has to determine the location of hidden bombs in a rectangular grid. Initially, each square on the grid is uncovered. When you uncover a square, one of three things can happen: 1) A bomb is at that square and you blow up and lose. 2) A number appears, signifying the number of bombs in adjacent squares. 3) The square is empty, signifying that there are no adjacent bombs. In the real minesweeper, when step 3 occurs, all of the adjacent squares are automatically cleared for you, since it's obviously safe to do so. And then, if any of those are clear, all of those adjacent squares are cleared, etc. Step 3 is recursive, since you apply the same set of steps to clear each adjacent square to a square with a "0" in it. I am going to simplify the code for this a bit so that we can focus on the recursive part of it. (I will replace some code with comments that simply signify what should be done in that portion of code.) The key here is ONLY if we clear a square and find 0 bombs adjacent to it do we make a recursive call. Furthermore, we make SEVERAL recursive calls, potentially up to 8 of them. public boolean domove(int row, int col) { // Take care of losing move if (realboard[row][col] == "*") { ... } // Take care of case where you've already uncovered that sq. else if (board[row][col] != '_') { ... } // Normal case else { // Finds # of adjacent bombs. int num = numberbombs(row,col); totalmoves--; //keeps track of total moves left to win. board[row][col] = (char)(num+48); //Changes board // to indicate move // Handles recursive case if (num == 0) { for (int i=-1; i < 2; i++) { for (int j=-1; j < 2; j++) { if (valid(row+i,col+j) && board[row+i][col+j]=='_') domove(row+i,col+j); } } } return false; } } Introduction to Towers of Hanoi The story goes as follows: Some guy has this daunting task of moving this set of golden disks from one pole to another pole. There are three poles total and he can only move a single disk at a time. Furthermore, he can not place a larger disk on top of a smaller disk. Our guy, (some monk or something), has 64 disks to transfer. After playing this game for a while, you realize that he's got quite a task. In fact, he will have to make 264 - 1 moves total, at least. (I have no idea what this number is, but it's pretty big...) Although this won't directly help you code, it is instructive to determine the smallest number of moves possible to move these disks. First we notice the following: It takes one move to move a tower of one disk. For larger towers, one way we can solve the problem is as follows: 1) Move the subtower of n-1 disks from pole 1 to pole 3. 2) Move the bottom disk to pole 2. 3) Move the subtower of n-1 disks from pole 3 to pole 2. We can now use this method of solution to write a method that will print out all the moves necessary to transfer the contents of one pole to another. Here is the prototype for our method: public static void towers(int n, int start, int end); n is the number of disks being moved, start is the number of the pole the disks start on, and end is the number of the pole that the disks will get moved to. The poles are numbered 1 to 3. Here is the method: public static void towers(int n, int start, int end) { if (n > 0) { int mid = 6 - start - end; towers(n-1, start, mid); System.out.print("Move disk "+n+" from tower "); System.out.println(start+" to tower "+end+"."); towers(n-1,mid,end); } } Recursive Method to Print Out a Number in Any Base Given a decimal number, consider printing it out in another base. For the ease implementation, let's assume the base is 10 or less. (In the text they show a small work around that works for bases up to 16, that could be extended to even larger bases.) The basic idea is as follows: Given a decimal number D in base B, we can determine the following: 1) The least significant digit 2) The value of the leftover number 1) This is simply D%B. 2) What is leftover can be represented as D/B, using integer division. Thus, if we are given a number with more than one digit in base B, we solve the problem recursively as follows: 1) Print out the whole number, except for the last digit. 2) Print out the last digit. Note that #1 is the recursive part of the algorithm. In code, we have: public static void printInt(int n, int base) { if (n >= base) printInt(n/base, base); System.out.print(n%base); } Modular Exponentiation The modular exponentiating problem is calculating xn mod p. Note that the answer will always be in between 0 and p-1, inclusive. Here is the standard iterative solution: public static int modexp(int x, int n, int p) { int ans = 1; for (int i=0; i<n; i++) ans = (ans*x)%p; return ans; } This looks like a perfectly acceptable solution, until you realize that in practice, instead of using ints, we may use BigInteger objects and that n could be as long as 50 or 60 digits. Here is one recursive solution to this problem: public static int modexp(int x, int n, int p) { if (n == 0) return 1; else if (n == 1) return x%p; else return (x*modexp(x,n-1,p))%p; } Is this any better than the iterative method above it? It is not. The reason is that each recursive call will spawn one more, and the chain or recursive calls is n calls long, just as our iterative loop runs n times. The problem is that n could simply be too big for this to be feasible. Now consider the following idea: If n is even, then we could instead calculate xn/2 mod p, and then square this one answer. If n is odd, do the usual recursion from above. Why is this idea a good one? Because, once we determine xn/2 mod p we are SAVING ourselves the work of recalculating it to perform the squaring operation!!! Here's the code: public static int modexp(int x, int n, int p) { if (n == 0) return 1; else if (n == 1) return x%p; else if (n%2 == 0) { int temp = modexp(x,n/2,p); return temp*temp%p; } else return x*modexp(x,n-1,p)%p } Now, we must assess the running time of this method. The first two methods ran in O(n) time, where n is the value of the exponent. (This assumes that multiplications take constant time, an assumption that isn't necessarily the case.) Notice that if n is even, we make one multiplication and one recursive call with an exponent of n/2. Thus, if we let T(n) be the running time of the algorithm, if n were even, we find: T(n) = T(n/2) + O(1), assuming a constant time multiply. BUT, there's a problem when n is odd! In this case, T(n) = T(n-1) + O(1). But wait! If n is odd, THEN n-1 is even. So, when n is odd, we really see that we do two multiplications and one recursive call with the exponent (n-1)/2, if we carry out the recursion one extra step. (Alternatively, we could have written the code to omit this extra recursive call.) So, really, if we assume that n/2 stands for an integer division, T(n) always satisfies: T(n) = T(n/2) + O(1), for both n being even AND odd. Now, let's solve this recurrence relation: T(n) = T(n/2) + O(1) = T(n/4) + O(1) + O(1) = T(n/8) + O(1) + O(1) + O(1) ... = T(n/2k) + O(1) + ... + O(1) -------------------- (O(1) appears k times) = kO(1), where n = 2k. Thus, k = log n. So, T(n) = O(log n) As you can see, this running time is far superior to that of the original two methods shown. Another way to look at this analysis is that the exponent gets divided by two for each two "iterations" of the recursion, so using the repeated halving rule, it must get down to 1 in 2logn iterations, where n is the exponent. Another way to solve the recurrence relation above: T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) ... T(2) = T(1) + O(1) ----------------------------- (Add all of these) RHS = T(n) + T(n/2) + ... + T(2) LHS = T(n/2) + ... + T(2) + T(1) + kO(1), where k = log n. These two sides are equal to one another. Set them equal and cancel out common terms on both sides, yielding: T(n) = T(1) + kO(1) = O(k), since T(1) = O(1). One final question: If we use the same construct for regular exponentiation (without modding), we find that the running time of THIS algorithm is hardly different than the original iterative one. BUT, when we mod, this algorithm is far, far quicker. WHY IS THAT? Euclid's Algorithm This algorithm is often described iteratively, but can just as easily be described recursively. Consider taking the gcd of 138 and 36. Our first step is: 138 = 3*36 + 30 In essence we divide 36 into 138 to yield a remainder of 30. Next, we calculate 36 = 1*30 + 6 Here we see that we just repeat the algorithm. Namely, the gcd(138, 36) = gcd(36, 30). Using this idea, we can find the gcd of two positive integers a and b as follows: public static int gcd(int a, int b) { if (b == 0) return a; else return gcd(b, a%b); } Why does this work, even when a is less than b? Recursive Problem to Solve Write a recursive method to determine the number of 1s in the binary representation of a positive integer n. Here is the prototype: // Precondition: n > 0. public static int numOnes(int n);