Recursion yet another look To recurse is divine, to iterate is human.

29
Recursion yet another look To recurse is divine, to iterate is human

Transcript of Recursion yet another look To recurse is divine, to iterate is human.

Page 1: Recursion yet another look To recurse is divine, to iterate is human.

Recursionyet another

look

To recurse is divine, to iterate is

human

Page 2: Recursion yet another look To recurse is divine, to iterate is human.

“Do I Have to Use Recursion?”public static double pow(double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else { exponent = exponent - 1; return value * pow (value, exponent); }}

How many would have preferred to do this with a “for loop” structure or some other iterative solution?

How many think we can make our recursive method even faster than iteration?

Page 3: Recursion yet another look To recurse is divine, to iterate is human.

Nota BeneOur power function works through brute force recursion.

28 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2

But we can rewrite this brute force solution into two equal halves:

28 = 24 * 24

and24 = 22 * 22

and22 = 21 * 21

andanything to the power 1 is itself!

Page 4: Recursion yet another look To recurse is divine, to iterate is human.

And here's the cool part...

28 = 24 * 24

Since these are the same we don't have to calculate them both!

Page 5: Recursion yet another look To recurse is divine, to iterate is human.

AHA!

So the trick is knowing that 28 can be solved by dividing the problem in half and using the result twice!

So only THREE multiplication operations have to take place:

28 = 24 * 24

24 = 22 * 22

22 = 21 * 21

Page 6: Recursion yet another look To recurse is divine, to iterate is human.

"But wait," I hear you say!

You picked an even power of 2. What about our friends the odd numbers?

Okay we can do odds like this:

2odd = 2 * 2 (odd-1)

Page 7: Recursion yet another look To recurse is divine, to iterate is human.

"But wait," I hear you say!

You picked a power of 2. Let’s see an odd one!

Okay, how about 221

221 = 2 * 220 (The odd number trick)

220 = 210 * 210

210 = 25 * 25

25 = 2 * 24

24 = 22 * 22

22 = 21 * 21

Page 8: Recursion yet another look To recurse is divine, to iterate is human.

"But wait," I hear you say!

You picked a power of 2. That's a no brainer!

Okay how about 221

221 = 2 * 220 (The odd number trick)

220 = 210 * 210

210 = 25 * 25

25 = 2 * 24

24 = 22 * 22

22 = 21 * 21

That's 6 multiplications instead of 20 and it getsmore dramatic as the exponent increases

Page 9: Recursion yet another look To recurse is divine, to iterate is human.

The Recursive InsightIf the exponent is even, we can divide and conquer so it can be solved in halves.

If the exponent is odd, we can subtract one, remembering to multiply the end result one last time.

We begin to develop a formula:

Pow(x, e) = 1, where e == 0 Pow(x, e) = x, where e == 1 Pow(x, e) = Pow(x, e/2) * Pow(x,e/2), where e is even Pow(x, e) = x * Pow(x, e-1), where e > 1, and is odd

Page 10: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value;

}

We have the same basetermination conditions

as before, right?

Page 11: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) {

}

This little gem determines if a number is odd or even.

Page 12: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { exponent = exponent / 2;

}

We next divide the exponent in half.

Page 13: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { exponent = exponent / 2; double half = pow (value, exponent);

}

We recurse to find that half of the brute force

multiplication.

Page 14: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { exponent = exponent / 2; double half = pow (value, exponent); return half * half;

}

And return the twohalves of the equation

multiplied by themselves

Page 15: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { exponent = exponent / 2; double half = pow (value, exponent); return half * half; } else { exponent = exponent - 1;

}

If the exponent is odd,we have to reduce it

by one . . .

Page 16: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { exponent = exponent / 2; int half = pow (value, exponent); return half * half; } else { exponent = exponent - 1; double oneless = pow (value, exponent);

}

And now the exponent iseven, so we can just

recurse to solve that portionof the equation.

Page 17: Recursion yet another look To recurse is divine, to iterate is human.

Solution #2public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { exponent = exponent / 2; int half = pow (value, exponent); return half * half; } else { exponent = exponent - 1; double oneless = pow (value, exponent); return oneless * value; }} We remember to multiply the value

returned by the original value, since we reduced the exponent by one.

Page 18: Recursion yet another look To recurse is divine, to iterate is human.

Recursion vs. Iteration:Those of you who voted for an iterative solution are likely going to produce:

O(N)

In a Dickensian world, you would be fired for this.

While those of you who stuck it out with recursion are now looking at:

O(log2n)

For that, you deserve a raise!

Page 19: Recursion yet another look To recurse is divine, to iterate is human.

The Cost of Recursion

The stack is important – it’s no joke. Computer hardware, it’s a fact of life. We have to live with it.

procedure CountToTen (count iot in Num) if (count <= 10) then print (count) // work CountToTen (count + 1) // recurse endifendprocedure //CountToTen

CountToTen: count=7

78

CountToTen: count=8

procedure CountToTen (count iot in Num) if (count <= 10) then print (count) // work CountToTen (count + 1) // recurse endifendprocedure //CountToTen

Page 20: Recursion yet another look To recurse is divine, to iterate is human.

The Cost of Recursionpublic class FibTest {

public static int fib (int num) {if (num == 0) return 0;else if (num == 1) return 1;else return fib(num-1) + fib(num-2);

}

public static void main(String[] args) {for (int i=0; i < 10; i++) System.out.println (fib(i));

}

}// FibTest

Each time you call a method, a new “activation frame”is created. This frame has unique copies of the parameters (Java has IN parameters), and unique copies of any local variables.

public class FibTest {

public static int fib (int num) {if (num == 0) return 0;else if (num == 1) return 1;else return fib(num-1) + fib(num-2);

}

public static void main(String[] args) {for (int i=0; i < 10; i++) System.out.println (fib(i));

}

}// FibTest

public class FibTest {

public static int fib (int num) {if (num == 0) return 0;else if (num == 1) return 1;else return fib(num-1) + fib(num-2);

}

public static void main(String[] args) {for (int i=0; i < 10; i++) System.out.println (fib(i));

}

}// FibTest

Page 21: Recursion yet another look To recurse is divine, to iterate is human.

Recursion Review

Recursion is defining a program in such a way that it may call itself.

Central to this understanding was the notion of a stack of successive calls.

Each recursivecall creates anew stackframe

time

stackheight

Page 22: Recursion yet another look To recurse is divine, to iterate is human.

public int fact ( int num ){ if (num == 0) return 1; else return num * ( fact (num -1) ); }

Let’s trace this for 4!Let’s trace this for 4!

fact (4) 4 *

fact (3)

4 *

3 *

fact (2)

4 *

3 *

2 *

fact (1)

4 *

3 *

2 *

1 *

fact (0)

4 *

3 *

2 *

1 *

1

Example

1

1

2

624

Page 23: Recursion yet another look To recurse is divine, to iterate is human.

fact (4) 4 *

fact (3)

4 *

3 *

fact (2)

4 *

3 *

2 *

fact (1)

4 *

3 *

2 *

1 *

fact (0)

4 *

3 *

2 *

1 *

1

public int fact ( int num ){ if (num == 0) return 1; else return num * ( fact (num -1) ); }

ProblemsProblemsWe don’t calculate anything until the final recursive call.

We must save a copy of each call, until we reach the end and ‘unwind’ the recursive call.

This is an exampleof augmentative(head) recursion.

Memory usage grows at O(n).

This gets expensive!

Page 24: Recursion yet another look To recurse is divine, to iterate is human.

Analyzing Augmentative Recursionpublic int fact ( int num ){ if (num == 0) return 1; else return num * ( fact (num -1) ); }

The culprit is a recursive call that returns what gets returned from a successive recursive call (which depends on what gets returned from a recursive call, etc., etc.) We leave the multiply hanging…

This requires us to save each level of the stack.

How can we rewrite this to avoid having to save the state of each frame?

AHA!AHA!

Page 25: Recursion yet another look To recurse is divine, to iterate is human.

Tail RecursionTail Recursion

• Eliminates the one big problem with augmentative

recursion: Massive memory use.

• Requires a compiler/interpreter that recognizes

when a module is finished executing. (i.e. the last

action is a recursive call and there’s no more work

to do.)

• Requires a slightly different style of recursive

module writing.

Page 26: Recursion yet another look To recurse is divine, to iterate is human.

Tail Recursion

public int fact (int product, int count, int max){ if (count == max) return product; else { count = count + 1; product = product * count; return fact (product, count, max); } } These values are all

precalculated; no needto save each frame!

These values are allprecalculated; no need

to save each frame!

Page 27: Recursion yet another look To recurse is divine, to iterate is human.

Tail Recursion

public int fact(int num){ if (num == 0) return 1; else return fact (1, 1, num); } public int fact (int product, int count, int max){ if (count == max) return product; else { count++; product *= count; return fact (product, count, max); } }

Method overloadingtakes place of

“helper” method

Method overloadingtakes place of

“helper” method

Need to “prime thepump” with goodstarting values.

Need to “prime thepump” with goodstarting values.

These values are allprecalculated; no need

to save each frame!

These values are allprecalculated; no need

to save each frame!

Page 28: Recursion yet another look To recurse is divine, to iterate is human.

24 4 46 3 42 2 4

Tracing Tail Recursion public int fact(int num){ if (num == 0) return 1; else return fact (1, 1, num); } public int fact (int product, int count, int max){ if (count == max) return product; else { count++; product *= count; return fact (product, count, max); } }

fact(4)

No stack buildup!No stack buildup!

Each recursivecall makes

calculationsindependent

of the prior recursive calls. There’s no need to save the state

of each call

1 1 4

Page 29: Recursion yet another look To recurse is divine, to iterate is human.

Recursion Roundup

Tail and Augmentative recursion are nothing more than fancy terms for different structures of recursive methods.

Remember, tail recursion means that the last thing that happens in a module is simply the return of a recursive call.

Tail: return fact(...);Head: return n * fact(...);