Recursion

Fabulously complex structure, simple procedures

Textbook: Chapter 13

Definition

You might ask, ``Recursion?? What's that?''

Recursion is the concept of well-defined self-reference.

 

Recursive definitions

A visual example: a Sierpinski gasket is three half-sized Sierpinski gaskets arranged in a triangle.

 

Recurrences

Recursion turns up in mathematics all the time!

Recurrences are recursive mathematical formulas.

Example:

       { 1                   if n = 1 or n = 2
F(n) = {
       { F(n - 2) + F(n - 1) otherwise
This is the definition of the famed Fibonacci sequence
n1234567...
F(n)11235813...

But recursion is just as much a computer science concept!

 

Computing Fibonaccis

Let's write a program to compute a Fibonacci number.

int fib(int which) {
    if(which <= 2) {
        return 1;
    } else {
        return fib(which - 2) + fib(which - 1);
    }
}

We can visualize the status of a recursive computation at any given time using a call stack:

	(fib 2)
	(fib 3)
	(fib 5)
	-------

We can visualize the entire recursive computation recursion tree, which looks something like this:

The base case

You might ask, ``Couldn't a recursive algorithm go on forever?''

Every recursive algorithm must have a case in which it does not recurse -- called the base case. (Just like induction!)

 

Another example: Exponentiation

We can also compute x to the n (for integer n) using a recursive definition:

		x to n = x * (x to n-1)
This suggests an obvious recursive Java method:
	public static double exponentiate (double x, int n) {
	    if(n == 0) {
	        return 1.0;
	    } else {
	        return x * exponentiate(x, n - 1);
	    }
	}
But this is not very efficient; for n of 1000, this has 1000 recursive calls of the method.

A better recursive Exponentiation algorithm

Notice that we can come up with a different recurrence for exponentiation:
	if n even:    x to n = (x to 2) to n/2
	if n odd:     x to n = x * (x to 2) to (n-1)/2
This suggests a different recursive Java method:
	public static double exponentiate (double x, int n) {
	    if(n == 0) {  // base case
	        return 1.0;
	    } else if(n % 2 == 0) {  // then if n is even
	        return exponentiate(x * x, n / 2)
	    } else {  // otherwise n is odd

	        return x * exponentiate(x * x, (n - 1) / 2);
	    }
	}
How deep does this recursion go?

Well, notice that at each step, the new value of n is at most 1/2 of the old value.
So, after k steps, the new n would be ( n ((1/2) to k) ).

What if we go log2 n steps? What is the value of n for the bottom call?

	( n ((1/2) to log2 n) ) == 1
(Good guess!)

So after log2 n steps, there's just one more step, so the recursion depth is (log2 n) + 1.
This is much better than a depth of n.

 

Towers of Hanoi

   |       |       |
  $$$      |       |
 @@@@@     |       |
#######    |       |
---+-------+-------+---
   a       b       c

Goal: move towers from a to b.

Rules:

 

A recursive solution

  1. Move all but largest disk from a to c (recursively!).
  2. Move largest from a to b.
  3. Move all but largest to b (recursively).

Pseudocode:

Algorithm Hanoi(disk, src, dst, spare)
if disk = 0 then
  Move disk from src to dst.
else
  Hanoi(disk - 1, src, spare, dst)
  Move disk from src to dst.
  Hanoi(disk - 1, spare, dst, src)
end of if
To start, we call Hanoi(2, a, b, c).

We can use a call stack to trace this, or we can use a call tree:

              2,a,b,c
     1,a,c,b           1,c,b,a
0,a,b,c  0,b,c,a  0,c,a,b  0,a,b,c

 

Analysis of Hanoi

How much longer 'til the world ends?

Define a recurrence:

       { 1                   if n = 1
H(n) = {
       { H(n-1) + 1 + H(n-1) if n > 1
Solve:
nH(n)
11
23
37
415
531
::
Prove:
H(n) = 2n - 1

A REAL puzzle:
What's the best algorithm for moving n disks using 4 pegs?