When confronted with a new problem there are two questions you should ask: 1. Is this problem like,...

35
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

Transcript of When confronted with a new problem there are two questions you should ask: 1. Is this problem like,...

Page 1: 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.

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

Page 2: 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.

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.

Page 3: 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.

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

Page 4: 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.

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

Page 5: 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.

Towers of Hanoi Puzzle

Page 6: 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.

Towers of Hanoi Puzzle

Page 7: 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.

Towers of Hanoi Puzzle

Page 8: 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.

Towers of Hanoi Puzzle

Page 9: 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.

Towers of Hanoi Puzzle

Page 10: 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.

Towers of Hanoi Puzzle

Page 11: 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.

Towers of Hanoi Puzzle

Page 12: 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.

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.

Page 13: 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.

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

Page 14: 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.

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);

}

Page 15: 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.

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);

}

}

Page 16: 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.

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

Page 17: 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.

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

Page 18: 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.

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

Page 19: 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.

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

Page 20: 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.

Recursively Defined Functions

1 if n = 0

n! =

n * (n – 1) ! for all n > 0

 

Figure 2. The factorial function

 

The “bad”

Page 21: 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.

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

Page 22: 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.

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--;

Page 23: 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.

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

Page 24: 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.

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!

Page 25: 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.

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

Page 26: 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.

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);

}

Page 27: 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.

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

Page 28: 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.

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!

Page 29: 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.

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

Page 30: 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.

Initial State of List A

Page 31: 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.

State of List A during the first call to binary_search

Page 32: 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.

State of List A during the third call to

binary_search

Page 33: 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.

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);

}

Page 34: 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.

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> ::= “”

Page 35: 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.

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