When confronted with a new problem there are two questions you should ask: 1. Is this problem like,...
-
Upload
margaretmargaret-reynolds -
Category
Documents
-
view
213 -
download
0
Transcript of When confronted with a new problem there are two questions you should ask: 1. Is this problem like,...
When confronted with a new problem there are two questions you should ask:
•1. Is this problem like, or a special case of, a problem that I already know how to solve?
•2.Is this a problem of size n in one of its variables that I could solve if I knew the solution to an instance of the same problem (or instances of all such problems) of size less than n?
Recursion
Recursion
When the answer to the second question is “yes”, you will employ recursion.
There are three steps in formulating a recursive solution to a problem.
1. Formulate the solution (to a problem of size nn) in terms of the solution to the same problem of size less than nn.
2. Determine a “base case” (at nn = 0) where the solution to the problem is trivial.
3. Terminate recursion when this “base case” value is reached.
Recursion “rewards” procrastination by formulating the solution of a problem as a sequence of solutions to similar problems, and postponing the actual solution of any of these problems until they are trivial.
Recursion – Contents of Presentation
1. Recursion as a problem solving technique
2. Towers of Hanoi – An example of a recursive approach to problem solving.
3. Recursive Algorithms – The Good, the Bad, and the Ugly!
a) The “Good” – Raising a base to a power.
b) The “Bad” – Calculating N Factorial.
c) The “Ugly” – The Fibonacci Sequence
4. Binary Search
5. Recursively Defined Grammars – Finding Palindromes
Example: Towers of Hanoi puzzle
In this puzzle, the player begins with n disks of decreasing diameter placed one on top of the other on one of three pegs of the game board. The player must move the disks from peg to peg, using each of the three pegs, until the entire tower is moved from the starting peg to one of the others. The only rule governing the movement of the disks is that in each move a disk of larger diameter must never be placed on top of one of smaller diameter
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
Towers of Hanoi Puzzle
A solution to the problem of moving a tower of size n from the source peg to the destination peg using a spare peg is found by moving a tower of size n – 1 from the source peg to the spare peg, moving the bottom disk from the source peg to the destination peg, and finally moving a tower of size n – 1 from the spare peg to the destination peg.
public class TowersOfHanoi {
private int num_disks;
private int source, spare, dest; //pegs
public TowersOfHanoi (int n_disks) {//see next slide}
//pre-condition: n_disks > 0
//exception: error message sent and program terminates
public void moveTower(int disks, int from_peg, int to_peg, int use_peg) {…}
public void moveDisk(int num, int from_peg, int to_peg) {…}
};
Class TowersOfHanoi -- header file
Class TowersOfHanoi -- implementation
Class Constructor
public TowersOfHanoi(int n_disks) {
if ( n_disks < 1) {
System.err.println( “error – must start with 1 or more disks in a tower”;
exit(1);
}
num_disks = n_disks;
source = 1; spare = 2; dest = 3;
moveTower(num_disks, source, dest, spare);
}
Class TowersOfHanoi -- implementation
moveTower
public void moveTower(int disks, int from_peg, int to_peg, int use_peg) {
if (disks == 1)
moveDisk(disks, from_peg, to_peg);
else {
moveTower(disks – 1, from_peg, use_peg, to_peg);
moveDisk(disks, from_peg, to_peg);
moveTower(disks –1, use_peg, to_peg, from_peg);
}
}
moveDisk
public void moveDisk(int num, int from_peg, int to_peg) {
System.out.print( “moving disk number ” + num + “ from peg”) ;
System.out.println(from_peg + “ to peg ” + to_peg );
}
Class TowersOfHanoi -- implementation
public void moveTower(int disks, int from_peg, int to_peg, int use_peg) {
if (disks == 1)
moveDisk(disks, from_peg, to_peg);
else
moveTower(disks-1, from_peg, use_peg, to_peg);
moveDisk(disks, from_peg, to_peg);
moveTower(disks-1, use_peg, to_peg, from_peg);
}
1
1
2
Disk source target
2
3
moveTower(3,1,3,2);1
1
moveTower(2,1,2,3);2
moveTower(1,1,3,2);3
3
1 1 3
2
2 1 2
2
moveTower(1,3,2,1);3
3
3
1 3 2
1
3 1 3
1
2
moveTower(2,2,3,1);2
2
moveTower(1,2,1,3);33
3
1 2 1
2
2 2 3
2
3moveTower(1,1,3,2);3
3
1 1 3Stack of act. Recs.
screen
Illustration of moveTower operation
Calls to moveTower
Recursively Defined Functions
Exponentiation --”The Good”
The Good, the bad, and the ugly
1 if n = 0
1/ power(b, |n|) if n < 0
power (b, n) = b * power (b, n-1) if n > 0 and odd
power(b, n/2) * power(b,n/2)if n > 0 and even
public double power (double b, int n) {
//start all recursive functions with a test for the base case
//test to terminate recursion)
if ( n == 0) return 1;
if (n < 0) return 1/power(b, -n);
if ((n % 2) == 1) return b * power(b, n-1);
else { // n even and positive
double temp = power(b, n/2);
return temp * temp;
}
}
Recursive power function
The recursive algorithm completes after at most 2*lg(n) calls
Recursively Defined Functions
1 if n = 0
n! =
n * (n – 1) ! for all n > 0
Figure 2. The factorial function
The “bad”
Recursively Defined Functions
The Factorial Function
public long factorial (int n) {
//precondition: n >= 0
if (n < 0) {
System.out.println( “error – factorial function requires non-negative argument\n”;
exit(1);
}
if (n == 0)
return 1;
else
return n * factorial (n – 1);
}
Requires n time (and space) consuming function calls
Recursively Defined Functions
Tail Recursion
public void reverse_write(const char [ ] A, int size) {
if (size > 0) {
System.out.print( A[size – 1]);
reverse_write (A, size – 1);
}
}
The recursive factorial algorithm uses recursion as the last statement in the function, only to set the parameters for the next round of processing. This is better done by iteration! Consider this example.
while(size > 0)
size--;
public void iterative_reverse_write(const char A[ ], int size) {
while (size > 0) {
System.out.println( A[size – 1]);
size--;
}
}
Recursively Defined Functions
Eliminating Tail Recusion
Recursively Defined Functions
public long iterative_factorial (int n) {
//precondition: n >= 0
if (n < 0) {
System.out.println( “error – factorial function requires non-negative argument”);
exit(1);
}
long prod = n;
while ( n > 1) {
n--;
prod *= n;
}
return prod;
}
Non-recursive factorial function
n iterations, but much faster and uses less memory than n recursive calls!
Recursively Defined Functions
The “ugly” – the fibonacci function
0 if n = 0
fibon (n) = 1 if n = 1
finbon(n-1) + fibon(n-2) if n > 1
Recursive fibonacci function
public long fibon (long int n) {
//precondition n >= 0;
//exception: if n send an error message and terminate
if ( n < 0 ) {
System.out.println( “error – fibonacci numbers not defined over negative numbers”);
exit (1);
}
if (n = 0) return 0; //base cases
if (n = 1) return 1;
return fibon(n-1) + fibon(n-2);
}
The fibonacci function
The run-time behavior of this recursive algorithm becomes exponential because the algorithm is “senile”. It “forgets” the value it obtained on a previous call and has to (recursively) recalculate the same value again.
fibon(6)
fibon(5)
fibon(4)
fibon(3)
fibon(2)
fibon(1)
1
+ fibon(0)
0
1
fibon(1)+
2
1
fibon(2)+
fibon(1)1
+ fibon(0)0
1
3+ fibon(3)
fibon(2)
fibon(1)1
+ fibon((0)
0
1+ fibon(1)
1
2
5+ fibon(4)
fibon(3)
fibon(2)
fibon(1)1
+ fibon(0)
0
1+ fibon(1)
1
2+ fibon(2)
fibon(1)1
+ fibon(0)0
1
3
8
An iterative fibonacci functionpublic long iterative_fibon(long int n) {
//precondition n >= 0;
//exception: if n send an error message and terminate
if ( n < 0 ) {
System.out.println( “error – fibonacci numbers not defined over negative numbers”);
exit (1);
}
if (n = 0) return 0; //base cases
if (n = 1) return 1;
long int term1 = 1, term2 = 0, hold;
for (int i = 2; i <= n; i++) {
hold = term1;
term1 += term2;
term2 = hold;
}
return term1;
}
Requires only 2 memory locations and n iterations!
Divide and Conquer
Binary Search
public int binarySearch(int [ ] A, int first, int last, int key) {..}
//preconditions: list A is ordered in increasing order (not checked)
// last > first (sub-list is not empty)
//post conditions: list A is unchanged, if key occurs in the list the index
// of (one of) the location where it occurs is returned, else
// a dummy value of –1 is returned
//exception: if first >= last send error message and terminate
Initial State of List A
State of List A during the first call to binary_search
State of List A during the third call to
binary_search
public int binarySearch(int [ ] A, int first, int last, int key) {
//check for exception
if (first >= last) {
System.out.println( “error – list to be searched is empty”);
exit(1);
} //check for termination of recursion
if ((last – first) == 1)
if (A[first] == key)
return first;
else
return –1;
int med = (first + last) / 2; //integer division
if (key < A[med] )
binarySearch (A, first, med, key);
else
binarySearch (A, med, last, key);
}
Recursively Defined Grammars
Palindrome
L = {w | w is a string of alphabetic characters that is the same read backwards or forwards}
and the BNF grammar G is:
<palindrome> ::= <empty_string> | <alphabetic> | “a” <palindrome> “a” |…| < “Z” <palindrome> “Z”
<alphabetic> ::= “a” | … | “z” | “A” | … | “Z”
<empty_string> ::= “”
public boolean isPalindrome(String s) { if ( s == NULL)
return TRUE; if (s.length == 1)
return TRUE; int len = s.length; return boolean (s.charAt(0) == s.charAt([len-1])) &&
isPalindrome(s.substring(1,len-2));}
Test for a palindrome