Você está na página 1de 35

Recursion

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
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 n) in terms of


the solution to the same problem of size less than n.

2.

Determine a base case (at n = 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.

Class TowersOfHanoi

-- header file

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

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

Class TowersOfHanoi

-- implementation

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

Illustration of moveTower operation


1

public void moveTower(int disks, int from_peg, int to_peg, int use_peg) {
if (disks == 1)

33 3
3
2
1

moveDisk(disks, from_peg, to_peg);


else

2
21 2
2
2
1

moveTower(disks-1, from_peg, use_peg, to_peg);


moveDisk(disks, from_peg, to_peg);
moveTower(disks-1, use_peg, to_peg, from_peg);

3
2

moveTower(3,1,3,2);

22
33
3

moveTower(2,1,2,3);
moveTower(2,2,3,1);
moveTower(1,1,3,2);
moveTower(1,2,1,3);
moveTower(1,3,2,1);

1
Stack of act. Recs.

Calls to moveTower

screen

Disk source target


1

1
3
1
2
1

3
1
2
2
1

2
3
1
3
3

Recursively Defined Functions


The Good, the bad, and the ugly

Exponentiation --The Good

power (b, n)

if n = 0

1/ power(b, |n|)

if n < 0

b * power (b, n-1)

if n > 0 and odd

power(b, n/2) * power(b,n/2)


if n > 0 and even

Recursive power function


The recursive algorithm completes after at most 2*lg(n) calls
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;
}
}

Recursively Defined Functions


The bad

n! =

if n = 0

n * (n 1) !

for all n > 0

Figure 2. The factorial function

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
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.
public void reverse_write(const char [ ] A, int size) {
if (size > 0) {

while(size > 0)

System.out.print( A[size 1]);


reverse_write (A, size 1);
}
}

size--;

Recursively Defined Functions


Eliminating Tail Recusion

public void iterative_reverse_write(const char A[ ], int size) {


while (size > 0) {
System.out.println( A[size 1]);
size--;
}
}

Recursively Defined Functions


Non-recursive factorial function
public long iterative_factorial (int n) {
//precondition: n >= 0
if (n < 0) {
System.out.println( error factorial function requires nonnegative argument);
exit(1);
}
long prod = n;
while ( n > 1) {
n iterations, but much faster
n--;
and uses less memory than
prod *= n;
n recursive calls!
}
return prod;
}

Recursively Defined Functions

The ugly the fibonacci function

fibon (n) =

if n = 0

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)

5
2

fibon(4)

1 1

fibon(3) + fibon(2)
fibon(2)
1

1 1

fibon(1) + fibon(0)

fibon(4)
2

fibon(3)

fibon(3)
11

+
00

fibon(1)
fibon(2)

fibon(2)

11

fibon(1)
fibon(1)

fibon(2) + fibon(1)
fibon(1)
+ fibon(0)
+ fibon((0)
fibon(1)
fibon(1)
1

fibon(5)
3

fibon(0)

0
+

fibon(0)

An iterative fibonacci function


public 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;
Requires only 2 memory
term1 += term2;
locations and n iterations!
term2 = hold;
}
return term1;
}

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

Test for a palindrome

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

Você também pode gostar