n! = n * (n-1) * (n-2) * . . . * 3 * 2 * 1, n > 0
From this formula, one can write the following Java function:
public int fact(int n) {
int res = 1;
for (int i = n; i > 1; i++)
res = res*i;
}
However, the formula above has problems: it isn't exactly precise
(the three dots), and it doesn't work for n =2, say.
Instead, mathematicians would generally use the following definition:
n! = 1, if n = 1,
n! = n * (n-1)!, if n > 1
This definition is recursive (refers to itself), and amazingly,
it can be directly implemented in Java:
public int fact(int n) {
if (n == 1) return 1;
return n * fact(n-1);
}
n = 93. Repeatedly divide by 8 and take the remainder.
Then repeat with the quotient by 8. Stop at 0 quotient.
93%8 == 5 (remainder) and 93/8 == 11 (quotient)
11%8 == 3 (remainder) and 11/8 == 1 (quotient)
1%8 == 1 (remainder) and 1/8 == 0 (quotient)
The numbers in boldface (the remainders) are the base 8 digits
of 93, generated backwards. We want to write the forwards,
to get 135 (base 8), and for this purpose, we can push them
on a stack, and pop them off, as in the following code:
public void writebase(int n) {
System.out.print(n + " in base 8 = ");
while (n > 0) {
push(n%8);
n = n/8;
}
while (!empty())
System.out.print(pop());
System.out.println("(base 8)");
}
Here is complete code to carry out the above program.
(The code includes the "simplest possible" stack, to avoid ugly
details.)
// BaseConversion: comvert to base 8
public class BaseConversion {
public int[] s = new int[100]; // storage for the stack
public int top = 0; // points one above the top of the stack
public int pop() { return s[--top]; } // pop and return top item
public void push(int n) { s[top++] = n; } // push another int
public boolean empty() { return top == 0; } // is stack empty?
public void writebase(int n) {
while (n > 0) {
push(n%8);
n = n/8;
}
while (!empty())
System.out.print(pop());
}
public static void main(String[] args) {
BaseConversion baseConversion = new BaseConversion();
System.out.print("93 in base 8 = ");
baseConversion.writebase(93);
System.out.println(" (base 8)");
}
}
The output from this program is:
93 in base 8 = 135 (base 8)
However, there is a much simpler version of the writebase
function that uses recursion. Here it is enclosed in a class.
It has the same output as the previous program.
// BaseConvRecurs: comvert to base 8
public class BaseConvRecurs {
public void writebase(int n) {
if (n != 0) {
writebase(n/8);
System.out.print(n%8);
}
}
public static void main(String[] args) {
BaseConvRecurs baseConvRecurs = new BaseConvRecurs();
System.out.print("93 in base 8 = ");
baseConvRecurs.writebase(93);
System.out.println("(base 8)");
}
}
Notice that the recursive function is much simpler. (It is also
possible to make use of strings and concatenation to avoid the
stack and recursion.)Here is a handout that shows the recursive calls made to writebase in the above example. (This example shows the code for writebase in the C language, but the ideas are the same.
Here is a short recursive program to calculate them and to measure the elasped time (not the CPU time):
// Fibonacci: compute Fibonacci numbers
public class Fibonacci {
public static int f(int n) {
if (n <= 1) return n;
return f(n-1) + f(n-2);
}
public static void elapsedTime(long startTime) {
long stopTime = System.currentTimeMillis();
double elapsedTime = ((double)(stopTime - startTime))/1000.0;
System.out.println("Elapsed time: " + elapsedTime + " seconds");
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for (int i = 0; i< 20; i++)
System.out.println("n: " + i + "\tF(n): " + f(i));
System.out.println();
for (int i = 5; i< 50; i += 5) {
System.out.print("n: " + i + "\tF(n): " + f(i) + "\t");
elapsedTime(startTime);
}
}
}
With output (slightly edited):
n: 0 F(n): 0 n: 1 F(n): 1 n: 2 F(n): 1 n: 3 F(n): 2 n: 4 F(n): 3 n: 5 F(n): 5 n: 6 F(n): 8 n: 7 F(n): 13 n: 8 F(n): 21 n: 9 F(n): 34 n: 10 F(n): 55 n: 11 F(n): 89 n: 12 F(n): 144 n: 13 F(n): 233 n: 14 F(n): 377 n: 15 F(n): 610 n: 16 F(n): 987 n: 17 F(n): 1597 n: 18 F(n): 2584 n: 19 F(n): 4181 n: 5 F(n): 5 Elapsed time: 0.02 seconds n: 10 F(n): 55 Elapsed time: 0.032 seconds n: 15 F(n): 610 Elapsed time: 0.032 seconds n: 20 F(n): 6765 Elapsed time: 0.034 seconds n: 25 F(n): 75025 Elapsed time: 0.046 seconds n: 30 F(n): 832040 Elapsed time: 0.186 seconds n: 35 F(n): 9227465 Elapsed time: 1.63 seconds n: 40 F(n): 102334155 Elapsed time: 17.687 seconds n: 45 F(n): 1134903170 Elapsed time: 197.155 secondsContrast the above recursive calculation of the Fibonacci numbers with the following non-recursive version, whose code is much trickier and more error-prone:
// Fibonacci0: compute Fibonacci numbers without recursion
public class Fibonacci0 {
public static long f(long n) {
long f0 = 0, f1 = 1, f2 = -1;
for (long i = 1; i < n; i++) {
f2 = f0 + f1;
f0 = f1;
f1 = f2;
}
return f2;
}
public static void elapsedTime(long startTime) {
long stopTime = System.currentTimeMillis();
double elapsedTime = ((double)(stopTime - startTime))/1000.0;
System.out.println("Elapsed time: " + elapsedTime + " seconds");
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for (int i = 2; i< 20; i++)
System.out.println("n: " + i + "\tF(n): " + f(i));
System.out.println();
for (long i = 5; i< 95; i += 5) {
System.out.print("n: " + i + "\tF(n): " + f(i) + "\t");
elapsedTime(startTime);
}
}
}
The following output shows that none of these calculations take any
time to speak of. A calculation of the 90th Fibonacci number
using the previous method would take at least 10^11 seconds, or
over 3000 years, not allowing for the fact that arithmetic
using longs would be slower, and that computer memory
would not remotely permit the calculation.
n: 2 F(n): 1 n: 3 F(n): 2 n: 4 F(n): 3 n: 5 F(n): 5 n: 6 F(n): 8 n: 7 F(n): 13 n: 8 F(n): 21 n: 9 F(n): 34 n: 10 F(n): 55 n: 11 F(n): 89 n: 12 F(n): 144 n: 13 F(n): 233 n: 14 F(n): 377 n: 15 F(n): 610 n: 16 F(n): 987 n: 17 F(n): 1597 n: 18 F(n): 2584 n: 19 F(n): 4181 n: 5 F(n): 5 Elapsed time: 0.02 seconds n: 10 F(n): 55 Elapsed time: 0.032 seconds n: 15 F(n): 610 Elapsed time: 0.033 seconds n: 20 F(n): 6765 Elapsed time: 0.033 seconds n: 25 F(n): 75025 Elapsed time: 0.035 seconds n: 30 F(n): 832040 Elapsed time: 0.035 seconds n: 35 F(n): 9227465 Elapsed time: 0.035 seconds n: 40 F(n): 102334155 Elapsed time: 0.036 seconds n: 45 F(n): 1134903170 Elapsed time: 0.037 seconds n: 50 F(n): 12586269025 Elapsed time: 0.038 seconds n: 55 F(n): 139583862445 Elapsed time: 0.038 seconds n: 60 F(n): 1548008755920 Elapsed time: 0.038 seconds n: 65 F(n): 17167680177565 Elapsed time: 0.04 seconds n: 70 F(n): 190392490709135 Elapsed time: 0.04 seconds n: 75 F(n): 2111485077978050 Elapsed time: 0.04 seconds n: 80 F(n): 23416728348467685 Elapsed time: 0.041 seconds n: 85 F(n): 259695496911122585 Elapsed time: 0.041 seconds n: 90 F(n): 2880067194370816120 Elapsed time: 0.041 secondsThe moral here is important: recursion is a powerful and useful tool that every computer scientist must be familiar with. However, recursion is not always the appropriate method.
// ReverseString: test a recursive method to reverse a String
// Uses the String method substring.
// s.substring(i) goes from index i to the end.
// s.substring(i, j) goes from index i up to but not including index j
public class ReverseTest {
// recursive string reversal
public static String reverse(String s) {
if (s.length() == 1) return s;
char ch = s.charAt(0);
String res = reverse(s.substring(1)) + ch;
return res;
}
// recursive: get rid of unnecessary local variables
public static String reverse2(String s) {
if (s.length() == 1) return s;
return reverse(s.substring(1)) + s.charAt(0);
}
// recursive: work from the other end
public static String reverse3(String s) {
if (s.length() == 1) return s;
return s.charAt(s.length() - 1) +
reverse(s.substring(0, s.length() - 1));
}
// NON-RECURSIVE: jus concatenate chars in opposite order
public static String reverse4(String s) {
String res = "";
for (int i = 0; i < s.length(); i++)
res = s.charAt(i) + res;
return res;
}
public static void main(String[] args) {
String str = "The quick brown fox jumps over the lazy dog.";
System.out.println(str);
System.out.println(reverse (str));
System.out.println(reverse2(str));
System.out.println(reverse3(str));
System.out.println(reverse4(str));
}
}
Here is the output:
The quick brown fox jumps over the lazy dog. .god yzal eht revo spmuj xof nworb kciuq ehT .god yzal eht revo spmuj xof nworb kciuq ehT .god yzal eht revo spmuj xof nworb kciuq ehT .god yzal eht revo spmuj xof nworb kciuq ehT
// BinarySearch: test a recursive method to do binary search on an array
public class BinarySearch {
// need extra "helper" method to feed in correct parameters
public static int binarySearch(int[] a, int x) {
return binarySearch(a, x, 0, a.length - 1);
}
// recursive version requires extra low and high parameters
public static int binarySearch(int[ ] a, int x, int low, int high) {
if (low > high)
return -1;
int mid = (low + high)/2;
if (a[mid] == x)
return mid;
else if (a[mid] > x)
return binarySearch(a, x, low, mid-1);
else // last possibility: a[mid] < x
return binarySearch(a, x, mid+1, high);
}
public static void main(String[] args) {
int[] a = {12, 23, 27, 38, 42, 49, 55, 67, 68, 82, 88, 93};
System.out.println("12 in position " + binarySearch(a, 12));
System.out.println("93 in position " + binarySearch(a, 93));
System.out.println("38 in position " + binarySearch(a, 38));
System.out.println("67 in position " + binarySearch(a, 67));
System.out.println("24 in position " + binarySearch(a, 24));
System.out.println("8 in position " + binarySearch(a, 8));
System.out.println("99 in position " + binarySearch(a, 99));
}
}
Here is the output:
12 in position 0 93 in position 11 38 in position 3 67 in position 7 24 in position -1 8 in position -1 99 in position -1