30
Recursion … just in case you didn’t love loops enough …

Recursion … just in case you didn’t love loops enough …

  • View
    226

  • Download
    0

Embed Size (px)

Citation preview

Recursion

… just in case you didn’t love loops enough …

Recursion

• A recursive method is a method that contains a call to itself

• Often used as an alternative to iteration when iteration is awkward or “inelegant”

• Each recursive call is given a smaller portion of the original problem

• Last recursive call solves diminished problem without recursion

Example

// method writes digits of a non-negative number, stacked vertically

public void write_vertical(int n){

n = Math.abs(n);if (n < 10)

System.out.println(“” + n);else{

write_vertical(n / 10);System.out.println(“” + n % 10);

}}

Tracing recursive method write_vertical

1. write_vertical(52406); n=52406 n/10=5240

2. write_vertical(n/10) ; n=5240 n/10=524

3. write_vertical(n/10); n=524 n/10=52

4. write_vertical(n/10); n=52 n/10=5

5. write_vertical(n/10); n=5 Stop recursion!

Output:

6. System.out.println(“” + n);

5

2

4

0

67. System.out.println (“” + n%10);

8. System.out.println (“” + n%10);

9. System.out.println(n%10);

10. System.out.println (“” + n%10);

Elements of recursive method

• Base caseBase case: problem is simplified to the point where recursion becomes unnecessary: n < 10

• Variant expressionVariant expression: the part of the problem that changes, making the problem smaller: n

• Recursive callRecursive call: method calls itself: write_vertical (n / 10);

How recursion works

• Activation record: memory block that stores all the information a method needs to work:– values of local variables & parameters– where method should return to (so calling

method can resume execution)

How recursion works

• When a method call is encountered, execution of current method ceases

• Information for newly called method is stored in an activation record

• Method executes

How recursion works• If new method contains another method call, the

process just described repeats • Each recursive call generates its own activation

record– as each recursive call is encountered, previous

activation record is stored on run-time stack– when last call fails to generate a new activation

record, stacked calls are removed (in reverse of the order they were stored), and each process continues in succession

Remember, for a recursive method to be successful ...

• Must be a problem with one or more cases in which some subtasks are simpler versions of the original problem - use recursion for these

• Must also have one or more cases in which entire computation is accomplished without recursion (base case)

Another example: the powers method

• Suppose you wanted to find the value of Xn

• For most values of n, we can solve this iteratively using a simple loop:int answer = 1;for (int c = 1; c <= n; c++)

answer *= X;• We can take care of the cases of n=1 or

n=0 with simple if statements; but what about a negative value of n?

Finding a recursive solution

• We can observe that for any value of n, Xn is equal to X * X(n-1)

• Armed with this information, we can easily develop a recursive solution that covers all values of X and n

Recursive power method

double rpower (double X, int n){

if (X == 0)return 0;

else if (n == 0)return 1;

else if (n > 0)return X * rpower(X, n-1);

else // n < 0return 1 / rpower(X, -n);

}

A classic: the Towers of Hanoi

• Initial situation: a stack of donut-shaped disks stacked in in order of decreasing size from bottom to top on a wooden peg

• Object: move all the disks to a second peg, one at a time– third peg available as temporary holding area– at no time may a larger disk be placed on top

of a smaller disk

Solving towers of Hanoi

• Simplest case: tower of one disk -- move disk to destination peg

• With stack of two disks:– Move top disk to third peg– Move next disk to destination– Move disk from third peg to destination

Solving towers of Hanoi

• Simplest case: tower of one disk -- move disk to destination peg

• With stack of two disks:– Move top disk to third peg– Move next disk to destination– Move disk from third peg to destination

Solving towers of Hanoi

• Simplest case: tower of one disk -- move disk to destination peg

• With stack of two disks:– Move top disk to third peg– Move next disk to destination– Move disk from third peg to destination

Solving towers of Hanoi

• Simplest case: tower of one disk -- move disk to destination peg

• With stack of two disks:– Move top disk to third peg– Move next disk to destination– Move disk from third peg to destination

Solving towers of Hanoi

• Simplest case: tower of one disk -- move disk to destination peg

• With stack of two disks:– Move top disk to third peg– Move next disk to destination– Move disk from third peg to destination

Solving towers of Hanoi

• Stack of 3:– Move 2 disks from 1st peg to 3rd peg, using

method already described– Move 1 disk from 1st to 2nd– Move 2 disks from 3rd to 2nd

Solving towers of Hanoi

• When there are two or more disks to move, always use third peg

• With more than two disks, we can solve the problem recursively by recognizing that the pegs are interchangeable -- that is, any peg can be used as source, destination, or “spare”

Solving towers of Hanoi

• In general, for a stack of n disks:– Move n-1 disks from peg 1 to peg 3– Move 1 disk from peg 1 to peg 2– Move n-1 disks from peg 3 to peg 2

Towers of Hanoi

• The top N-1 disks must be moved to peg 2, allowing you to then move the largest disk from peg 1 to peg 3.

• You can then move the N-1 disks from peg 2 to peg 3.

• Only two pegs are involved in a single move; for this, we’ll employ a helper method, moveOne

public void towersOfHanoi (int N, char from, char to, char spare){

if (N == 1) {moveOne(from, to);

} else {towersOfHanoi(N-1, from, spare, to);moveOne (from, to);towersOfHanoi(N-1, spare, to, from);

}}private void moveOne(int from, int to) {

System.out.println(from + “--->” + to);}

Drawing pictures with recursion

• We used ASCII art to illustrate how iteration worked – we can do the same with recursion

• For example, if we wanted to draw a line using several instances of a single character, we could write a loop like the one below:for (int x = lineLen; x > 0; x--)

System.out.print(“$ ”);

• We can do the same task recursively; see next slide

Example

public void drawLine (int x){

if (x == 0)return;

System.out.print(“$”);drawLine(x-1);

}

Same example, slightly different

• The original iterative line drawing routine was different from typical examples we’ve seen in the past, in that the counter was counting down instead of up

• I did this to illustrate how the iterative version relates to the recursive version – the problem (value of counter) gets smaller with each loop iteration, or each recursive call

• The situation is the same if we have the counter counting up, but it’s a little harder to see

ExampleIterative version:for (int x = 0; x < someConstant; x++)

System.out.print (“$ ”);Equivalent recursive version:public drawLine (int x, int someConstant) {

if (x < someConstant) {System.out.print(“$”);drawLine (x+1, someConstant);

}}

Instead of x getting smaller, the distance betweendistance between x and someConstant gets smaller

When Not to Use Recursion

• When recursive algorithms are designed carelessly, it can lead to very inefficient and unacceptable solutions

• In general, use recursion if– A recursive solution is natural and easy to

understand.– A recursive solution does not result in excessive

duplicate computation.– The equivalent iterative solution is too complex.

Preventing infinite recursion

• One-level recursion: every case is either a stopping case or makes a recursive call to a stopping case

• Since most recursive functions are, or have the potential to be, recursive beyond just one level, need more general method for determining whether or not recursion will stop

Preventing infinite recursion

• Define a variant expressionvariant expression– numeric quantity that decreases by a fixed

amount on each recursive call– in towers of Hanoi, variant expression was

height -- each recursive call used height -1 as parameter

• Base case is when variant expression is less than or equal to its threshold valuethreshold value