Fabulously complex structure, simple procedures
Textbook: Chapter 13
You might ask, ``Recursion?? What's that?''
Recursion is the concept of well-defined self-reference.
A visual example: a Sierpinski gasket is three half-sized Sierpinski gaskets arranged in a triangle.
Recursion turns up in mathematics all the time!
Recurrences are recursive mathematical formulas.
Example:
This is the definition of the famed Fibonacci sequence{ 1 if n = 1 or n = 2 F(n) = { { F(n - 2) + F(n - 1) otherwise
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... |
---|---|---|---|---|---|---|---|---|
F(n) | 1 | 1 | 2 | 3 | 5 | 8 | 13 | ... |
But recursion is just as much a computer science concept!
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:
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!)
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.
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)/2This 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.
| | | $$$ | | @@@@@ | | ####### | | ---+-------+-------+--- a b c
Goal: move towers from a to b.
Rules:
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 ifTo 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
How much longer 'til the world ends?
Define a recurrence:
Solve:{ 1 if n = 1 H(n) = { { H(n-1) + 1 + H(n-1) if n > 1
Prove:
n H(n) 1 1 2 3 3 7 4 15 5 31 : :
A REAL puzzle:
What's the best algorithm
for moving n disks using 4 pegs?