[ Home | Syllabus |Course Notes]
Many problems can be divided into two parts:
This divide and conquer problem solving strategy involves:
A Journey
Loops are a classic example of this
strategy.
To sum the values in an array.
Proof by Induction is another example
Recursion is yet another example
NOTE: recursions are often computed backwards of loops. Loops usually go from Base to N. Recursions usually go from N to Base. But in practice, the implementation of recursion often goes like the loop.
Here the post condition of the resursive procedure is like the loop invariant.
Many mathematical concepts are defined by a recurrence relationship which can be used to form a loop invariant or a recursion post condition.
Example: N factorial:
N! = N * (N-1) * (N-2) * ... * 1 = N * (N-1)!
The multiplication is the immediate step, (N-1)! is the smaller version of the same problem.
BASE CASE: 1! = 1 or 0! = 1
Leads to the following recursive
definition
(Recursive means defined in terms of itself - a kind of circular
defintion - except the base case and getting smaller breaks the
circle).
factorial(N) = 1, if N = 0
factorial(N) = N * factorial(N-1), if N > 0
Which leads (naturally) to the following recursive program:
// Chap 2, p 55 int Fact(int N) // --------------------------------------------------- // Computes the factorial of a nonnegative integer. // Precondition: N must be greater than or equal to 0. // Postcondition: Returns the factorial of N; N is // unchanged. // --------------------------------------------------- { if (N == 0) return 1; else return N * Fact(N - 1); } // end Fact
As an example, compute Fact(3)
How does the computer handle suspending and reactivating recursive function calls?
For the example given above:
click here for complete program
// Chapter 2, p 62 #include <string.h> const int MAX_LENGTH = 30; typedef char stringType[MAX_LENGTH+1]; void WriteBackward(stringType S, int Size) // --------------------------------------------------- // Writes a character string backward. // Precondition: The string S contains Size // characters, where Size >= 0. // Postcondition: S is written backward, but remains // unchanged. // --------------------------------------------------- { if (Size > 0) { // write the last character cout << S[Size-1]; // write the rest of the string backward WriteBackward(S, Size - 1); // Point A } // end if // Size == 0 is the base case - do nothing } // end WriteBackward
WriteBackward2(S) if (string is empty) Do nothing else { WriteBackward2(S minus first character) Write first character }
What do we know about this problem?
BaseCase: x^0 = 1
Extension Case: x^N = x*x^(N-1) , if N>0
This knowledge is the basis for both iterative (loop based) and recursive solution.
// Chap 2, p 71, p 72 // Demonstrates Pow1, Pow2, and Pow3. #include <iostream.h> int Pow1(int X, int N) // --------------------------------------------------- // Exponentiation function -- ITERATIVE SOLUTION // Precondition: X is an integer; N is a nonnegative // integer. // Postcondition: Returns X raised to the Nth power. // --------------------------------------------------- { int Temp = 1; // Base Case x^0 = Temp for (int Exponent = 1; Exponent <= N; ++Exponent) // Loop Invariant: x^(Exponent-1) = Temp Temp *= X; return Temp; } // end Pow1
int Pow2(int X, int N) // Exponentiation function -- RECURSIVE SOLUTION { if (N == 0) return 1; else return X * Pow2(X, N-1); } // end Pow2
Use some more knowledge about this
problem area
(x^y)*(x^z) = x^(y+z)
let y = z = N/2, then
(x^(N/2)) * (x^(N/2)) = x^N
e.g. (x^3) * (x^3) = x^6
This works for N even, for N odd, x^N = x*(x^((N-1)/2))*(x^((N-1)/2))
This is nice since we only
make one call to get each x^(N/2) and
the size of the problem is cut in half each time (instead of just
reducing by one
as in the previous solution).
For instance, in the above
algorithm x^8 = x*x^7=x*x*x^6=x*x*x*x^5 ...
which takes 9 recursive calls, (for N=9,8,7,6,5,4,3,2,1,0)
But with our new strategy
of halfing
x^8 = (x^4)*(x^4) = (x^2)*(x^2)*(x^2)*(x^2) = (x^1)*... =
x*x^0*...
which only takes 5 recursive calls! (for N=8,4,2,1,0)
int Pow3(int X, int N) // Exponentiation function -- MORE EFFICIENT // RECURSIVE SOLUTION { if (N == 0) return 1; else { int HalfPower = Pow3(X, N/2); if (N % 2 == 0) return HalfPower * HalfPower; // even N else return X * HalfPower * HalfPower; // odd N } // end else } // end Pow3
Click here to see complete program