Iteration and recursion are very related to each other. In iteration, it is natural to write a loop to repeat a task a certain amount of times. In recursion, it is natural to write a method that performs a task by calling itself to perform the same task!?!!?
For example,
the following program
shows two ways to count from a to b: an iterative
method and a recursive method.
import java.io.*;
public class SampleCount {
public static void main(String[] args) {
System.out.println("Iteratively counting");
iterativeCount(1,10);
System.out.println();
System.out.println("Recursively counting");
recursiveCount(1,10);
}
// Count iteratively from a to b
public static void iterativeCount(int a, int b) {
int i;
for (i = a; i <= b; i++) {
System.out.println(i);
}
}
// Count recursively from a to b
public static void recursiveCount(int a, int b) {
// This is the terminating condition
if (a > b) {
return;
}
// Print the number a
System.out.println(a);
// Now count from a+1 to b
recursiveCount(a+1,b);
}
}
Is (a > b)? No
Print out a ---> 1
Call recursiveCount(a+1,b) -> rC(2,10)
Is (a > b)? No
Print out a ---> 2
Call recursiveCount(a+1,b) -> rC(3,10)
... many steps later ...
Call recursiveCount(a+1,b) -> rC(10,10)
Is (a > b)? No
Print out a ---> 10
Call recursiveCount(a+1,b) -> rC(11,10)
Is (a > b)? Yes
Return
The
following program
shows how to do this recursively.
import java.io.*;
public class PrintDigits {
public static void main(String[] args) {
System.out.println("Please enter an integer");
int a = Keyboard.inputInteger();
System.out.println("Number " + a + " is ");
printInWords(a);
System.out.println();
}
// Print the digits of the number
public static void printInWords(int number) {
if (number < 10) {
// Just one digit... easy
System.out.print(digitToWord(number) + " ");
} else {
// Print out the "truncated part" first
printInWords(number/10);
System.out.print(digitToWord(number%10) + " ");
}
}
// Returns the word form of a digit (0-9)
public static String digitToWord(int digit) {
if ((digit >= 10) || (digit < 0)) {
System.out.println("Error: Not a digit");
System.exit(1);
}
return wordDigit[digit];
}
public static String[] wordDigit = {
"zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine"};
}
There are several ways to break up this problem. Here is one way:
Output all but the last digit (in words)
Output the last digit (as a word)
The first part is the recursive part of the solution, it is in
fact the exact same problem as the larger problem.
Why do we have the conditional at the beginning?
If not, the program would never end. Remove it and see.
This is called the terminating condition.
The case checked for is often called the stop case or
base case.
As with loops, you need to have some way to stop the whole process.
We had this in the first example, too.
Notice that once a > b you don't need to count any further.
Otherwise, it would continue counting up and up and up.
I will describe this process in class, since it is a bit complicated
to write down on paper. However, please refer to the book to
see how this process works as well.
Here is a summary of what happens.
Let us look at what happens with printInWords(123).
// Entering printInWords(number == 123)
is (number < 10)? No
call printInWords(number / 10)
// Entering printInWords(number == 12)
is (number < 10)? No
call printInWords(number / 10)
// Entering printInWords(number == 1)
is (number < 10)? Yes
call digitToWord(1)
returns "one"
print "one" + " "
return
// Exiting printInWords(number == 1)
// Returned: number == 12!?!
call digitToWord(number % 10)
returns "two"
print "two" + " "
return
// Exiting printInWords(number == 12)
// Returned: number == 123
call digitToWord(number % 10)
returns "three"
print "three" + " "
return
// Exiting printInWords(number == 123)
All recursive methods can be written as iterative methods. Sometimes this is easy, other times it involves a little thought.
Recursive methods are usually less efficient than iterative methods. Why? There is considerable overhead in making a method call. The state of the program has to be saved and values passed to the method before the method can actually be executed. So, in general method calls are slower. But, they do often make the program easier to read, write, and debug.
import java.io.*;
public class SamplePow {
public static void main(String[] args) {
int pow = 0;
System.out.println("Iteratively, 2**10 = ");
pow = iterativePow(2,10);
System.out.println(pow);
System.out.println("Recursively, 2**10 = ");
pow = recursivePow(2,10);
System.out.println(pow);
}
// Return a**b (iteratively)
public static int iterativePow(int a, int b) {
int i;
int total = 1;
for (i = 0; i < b; i++) {
total *= a;
}
return total;
}
// Return a**b (recursively)
public static int recursivePow(int a, int b) {
// This is the terminating condition
if (b <= 0) {
return 1;
}
// Compute a**(b-1), multiply by a,
// and return value
return recursivePow(a,b-1) * a;
}
}
You instead employ a quicker strategy. Programming is the same way. A computer is fast but if the programs aren't written efficiently enough, even a human could be faster. In CSC 220 and in CSC 517, you will learn about making programs efficient. The design of algorithms is critical to making efficient code.
Let us generalize the phone book problem. Imagine you are given a sorted list of numbers. A query is to identify if (and where) a given number exists in the list.
The naive approach which works but does not work effeciently would be to start at the beginning and check every number until the end. If it found the number, it would return the location, otherwise, it would say there was no result found.
A quicker approach would be to recursively do the following procedure. Search for the number between ranges a and b (initially the first and last element). Look at the middle value in the range mid=(a + b)/2 and see if that value equals the given search number. If not, it is either too large or too small. If it is too large, repeat the search with new values a and mid. If it is too small, repeat the search with new values mid+1 and b. Notice that after each step we cut the search in half. How many levels do we need to search to get to the solution? About log n where n is the initial size of the array i.e. initial value of b. Given time I will try to explain this operation further, but it depends on how much time is left in class.
The class BinarySearch is sample code for the improved version.
The class RecursiveExamples gives a few more examples of recursion. Test them out to see how they work and to get used to understanding recursion.