Notes - Recursion So far we have only learned how to solve problems iteratively using loops. We will now learn how to solve problems recursively by having a method call itself. A geeky definition of recursion is as follows: Recursion see Recursion. A Recursive algorithm has two parts: 1. If the problem is easy, solve it immediately. 2. If the problem can't be solved immediately, divide it into smaller problems, then: Solve the smaller problems by applying this procedure to each of them. We call a problem that can be solved immediately a base case and a problem that divides the problem into a smaller problem a recursive case. So a recursive algorithm is really made up of: 1. At least one base case. 2. At least one recursive case. Let s consider the mathematical concept factorial. In order to calculate a factorial (!), you multiply all the numbers from n down to 1, knowing that 1! = 1, 0! = 1. So, 5! = 5 * 4 * 3 * 2 * 1 10! = 10 * 9 * 7 * 6 * 5 * 4 * 3 * 2 * 1 3000! = 3000 * 2999 * 2998 * 2997 * * 3 * 2 * 1 We could also state 5! = 5 * 4 * 3 * 2 * 1 as: 5! = 5 * 4! 4! = 4 * 3! 3! = 3 * 2! 2! = 2 * 1! And we know 1! = 1 So, in general: n! = n * (n-1) * (n-2) * (n-3) * * 1 Which broken down to model a recursive algorithm is: If simple, Solve it immediately --> 1! = 1, 0! = 1 If not simple, solve a small piece and then try to solve the rest --> n! = n * (n-1)! If we were to divide this concept into a recursive definition, we might say: 1. If n = 1, return 1 2. If n > 1, return n * (n-1)!
We call this the static view of a recursive method. Basically the static view is the mathematical way of looking at a recursive problem. So, let's create a method that can calculate factorial. Inside this method, we need to create a case for the immediately solvable problem, and a case for the not simple solution. public int factorial(int n) if(n == 1) // base case: n! = 1 return 1; else // recursive case: n! = n * (n-1)! where n > 1 return n * factorial(n-1); Then let's try out our method. If we try to print the result 4!... public class FactorialTest public static void main(string[] args) System.out.println( 4! = + factorial(4)); public static int factorial(int n) if(n == 1) // base case: n! = 1 return 1; else // recursive case: n! = n * (n-1)! where n > 1 return n * factorial(n-1);...what really happens? Well, the first time that the factorial method is called (the first activation ), the parameter n is passed the value 4. The base case (n ==1) is not true, so we move on to the recursive case and say that factorial(4) = 4 * factorial(3).
But what s factorial(3)? We have to find out. So on the second activation, the value 3 is passed to n. The base case (n==1) is still not true, so move on to recursive case and say factorial(3) = 3 * factorial(2). But we don t know what factorial(2) is either. So we find out. On the third activation, the value 2 is passed to n, the base case is not true, and we say factorial(2) = 2 * factorial(1). Well, factorial(1) finally we ve reached a solvable case. On the fourth activation, the value 1 is passed to n, and we find that our base case is met (n == 1), so we simply return 1.
Now we can backtrack and solve 2! which was 2 * factorial(1) since we now know that factorial(1) is 1 so factorial(2) = 2 * 1 = 2. Now we can solve factorial(3) which was 3 * factorial(2) so factorial(3) = 3 * 2 = 6. Now we can solve our original problem: factorial(4) which was 4 * factorial(3) so 4! = 4 * 6 = 24.
So the result of factorial(4) is 24. So, why did we go through all this nonsense, when we could have just said: public int factorial(int n) int fact = 1; for(int i = n; i > 1; i--) fact *= n; return fact; Well, some problems are naturally recursive. If you can easily identify a base case and a recursive case, then a recursive method might be easier to write and understand. Sometimes a recursive method just makes more sense than an iterative method. All recursive methods can be written iteratively. However, recursive methods come at a cost. Recursive methods use a lot of memory because each activation requires more memory to be allocated for each parameter and local variable (unless the variable is static, of course). Consider the following: public int infinite(int n) return infinite(n+1); What s wrong with this? Where s the base case? If we forget to include a base case or our base case is unreachable, then we create infinite recursion. In Java, when we have infinite recursion, we get a StackOverFlowException. This means that we have created so many activations of the method that we have run out of system resources. As you can imagine, this is bad. Try out the following and observe the output: public class InfiniteTestMain public static void main(string[] args) System.out.println(infinite(1)); public static int infinite(int n) System.out.println(n); return infinite(n+1); At which activation does the system run out of resources? When writing a recursive method, try the following:
1. Think about how you can reduce the problem to one or more simpler sub-problems of the same form. 2. Think about what information you need to give to the sub-problems (the parameters). 3. Think about what information you want back from the sub-problems (the return type). 4. Write the method header. 5. Write a method specification (like the static view of the problem) that explains exactly what it will do in terms of the parameters. Include any preconditions. Think about the base case: When is the answer so simple that we know the answer without recursing? Think about the recursive case: How are we going to break the problem into a smaller problem (by calling out method again) and get closer to the base case? 6. Write the code. 7. Test out your code with several different cases. A basic recursive formula is: <visibilitymodifier> <returntype> <methodname>(<parameters>) if (<basecase>) // return simple solution else // divide the problem into sub-problems, // getting closer to the base case. // return the result of the simpler case Note: recursive methods may have more than one base case or more than one recursive case. Comprehension Questions: 1. What is a recursive method? 2. What are the two parts of a recursive method? 3. What is the static view of a recursive method? 4. What is a base case? 5. What is a recursive case? 6. What is an activation? 7. What is an activation chain? 8. Show the dynamic view of mystery(4) given the following definition of mystery: //precondition: n <= 10 public int mystery(int n) if(n == 10) return 1; else return 1 + mystery(n+2);
9. What does the method above do? 10. What is one advantage of using recursion? 11. What is one disadvantage of using recursion? 12. What is a StackOverflowException?