Fabulously complex structure through simple procedures
a b c | | | ### | | @@@@@ | | $$$$$$$ | | ----+-------+-------+----
We want to move the tower from a, moving one disk at a time, never moving a disk on top of a smaller one.
A simple procedure does this, if it calls itself. This self-use is recursion.
Algorithm Hanoi(disk, source, dest, other) if disk is smallest then move disk from source to dest else Hanoi(next disk, source, other, dest) move disk from source to dest Hanoi(next disk, other, dest, source) fi
IN Hanoi($$$$$$$, a, b, c) IN Hanoi(@@@@@, a, c, b) IN Hanoi(###, a, b, c) | | | | | | @@@@@ | | $$$$$$$ ### | ----+-------+-------+---- OUT | | | | | | | | | $$$$$$$ ### @@@@@ ----+-------+-------+---- IN Hanoi(###, b, c, a) | | | | | | | | ### $$$$$$$ | @@@@@ ----+-------+-------+---- OUT OUT | | | | | | | | ### | $$$$$$$ @@@@@ ----+-------+-------+---- IN Hanoi(@@@@@, c, b, a) IN Hanoi(###, c, a, b) | | | | | | | | | ### $$$$$$$ @@@@@ ----+-------+-------+---- OUT | | | | | | | @@@@@ | ### $$$$$$$ | ----+-------+-------+---- IN Hanoi(###, a, b, c) | | | | ### | | @@@@@ | | $$$$$$$ | ----+-------+-------+---- OUT OUT OUT
Algorithm Fast-Exponentiate(x, n) if n = 0 then return 1 else if n is even then return Fast-Exponentiate(x^2, n / 2) else return x * Fast-Exponentiate(x^2, (n - 1) / 2) fi
IN Fast-Exponentiate(2, 10) 10 is even: IN Fast-Exponentiate(4, 5) 5 is odd: IN Fast-Exponentiate(16, 2) 5 is odd: IN Fast-Exponentiate(256, 1) 5 is odd: IN Fast-Exponentiate(32768, 0) return 1 return 256 return 256 return 1024 return 1024
Correctness: We prove this using induction on n. The base case, when n is 0, is easy: Fast-Exponentiate(x, 0) = 1 = x^0. Say it works for all n smaller than k. Does it work for k? If k is even, then k/2 is smaller than k, so
Fast-Exponentiate(x, k) = Fast-Exponentiate(x^2, k / 2) = (x^2)^(k / 2) = x^(2(k/2)) = x^kIf k is odd, then (k - 1)/2 is smaller than k, so
Fast-Exponentiate(x, k) = x * Fast-Exponentiate(x^2, (k - 1) / 2) = x * (x^2)^((k - 1) / 2) = x * x^(2((k - 1)/2)) = x * x^(k - 1) = x^k
Time bound: We use a recurrence.
T(1) = 1 T(n) < T(n / 2) + 1Using the Master Theorem (see textbook appendix), we see that
T(n) = O(log_2 n)
Recursion and mathematical induction have a lot in common.
Like inductive proofs, recursive algorithms must have a base case - some situation where th efunction does not call itself.
Otherwise the program will never get to an answer!
public static int Factorial(int n) { if(n == 0) { return 1; } else { return n * Factorial(n - 1); } }
The Fibonacci sequence is the following:
1 1 2 3 5 8 13 21 34 55 ...Each number is the sum of the preceding two numbers in the sequence. We calculate the nth Fibonacci number.
public static int Fibonacci(int n) { if(n < 2) { return 1; } else { return Fibonacci(n - 1) + Fibonacci(n - 2); } }
We can graph what's happening using a call tree.
n = 4 ___/ \___ / \ n = 3 n = 2 / \ / \ n = 2 n = 1 n = 1 n = 1 / \ n = 1 n = 1
Notice that this takes Fibonacci(n) additions! Since Fibonacci(n) = O(1.618^n), this is a very poor running time. In fact one can calculate it in O(n) time.
Problem class FIND_IN_SORTED_ARRAY Input: an array arr sorted in increasing order, an integer n Output: index of n in arr, -1 if absent
Algorithm Binary-Search(arr, n, low, high) if high < low then if arr[low] = n then return low else return -1 fi else if arr[(low + high) / 2] < n then return Binary-Search(arr, n, low, (low + high) / 2) else if arr[(low + high) / 2] > n then return Binary-Search(arr, n, (low + high) / 2 + 1, high) else return (low + high) / 2 fi fi
This takes O(log_2 n) time. This is the solution to the recurrence relation
T(n) < T(n / 2) + O(1)
+----+----+----+----+----+----+----+----+ arr = | 2 | 3 | 5 | 7 | 11 | 13 | 17 | 19 | +----+----+----+----+----+----+----+----+ n = 13 IN Binary-Search(arr, 13, 0, 8) 11 < 13: IN Binary-Search(arr, 13, 5, 8) 17 > 13: IN Binary-Search(arr, 13, 5, 6) 13 = 13: RETURN 5 RETURN 5 RETURN 5
Each call to a recursive function gets its own copy of variables.
Changing a variable does not change it for higher recursive calls.
public static int SumSquares(int n) { int n_squared; int others; if(n < 1) { return 0; } else { n_squared = n * n; others = SumSquares(n - 1); return n_squared + others; } }
The call tree looks like this.
returns 5 n = 2 n_squared = 4 | | returns 1 n = 1 n_squared = 1 | | returns 0 n = 0When the call with n=1 changes n_squared, it just changes its copy of n_squared. The n_squared for the call with n=2 remains unchanged.