Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "Recursion yet another look To recurse is divine, to iterate is human."— Presentation transcript:

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

2 “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?

3 Nota Bene Our power function works through brute force recursion. 2 8 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 But we can rewrite this brute force solution into two equal halves: 2 8 = 2 4 * 2 4 and 2 4 = 2 2 * 2 2 and 2 2 = 2 1 * 2 1 and anything to the power 1 is itself!

4 And here's the cool part = 2 4 * 2 4 Since these are the same we don't have to calculate them both!

5 AHA! So the trick is knowing that 2 8 can be solved by dividing the problem in half and using the result twice! So only THREE multiplication operations have to take place: 2 8 = 2 4 * = 2 2 * = 2 1 * 2 1

6 "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: 2 odd = 2 * 2 (odd-1)

7 "But wait," I hear you say! You picked a power of 2. Let’s see an odd one! Okay, how about = 2 * 2 20 (The odd number trick) 2 20 = 2 10 * = 2 5 * = 2 * = 2 2 * = 2 1 * 2 1

8 "But wait," I hear you say! You picked a power of 2. That's a no brainer! Okay how about = 2 * 2 20 (The odd number trick) 2 20 = 2 10 * = 2 5 * = 2 * = 2 2 * = 2 1 * 2 1 That's 6 multiplications instead of 20 and it gets more dramatic as the exponent increases

9 The Recursive Insight If 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

10 Solution #2 public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; } We have the same base termination conditions as before, right?

11 Solution #2 public 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.

12 Solution #2 public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { } public static int isOdd(int num){ if (num==0) return 1; else return isEven(num-1); } public static int isEven(int num){ if (num==0) return 0; else return isOdd(num-1); } Don’t like the mod operator? Get used to it. The alternative is something mutually recursive and wacky, like this:

13 Solution #2 public static double pow (double value, int exponent){ if (exponent == 0) return 1D; else if (exponent == 1) return value; else if (exponent % 2 == 0) { } public static int isOdd(int num){ if (num==0) return 1; else return isEven(num-1); } public static int isEven(int num){ if (num==0) return 0; else return isOdd(num-1); } That mod operator is starting to look a lot better, eh? It merely tests if the number is evenly divided by two.

14 Solution #2 public 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.

15 Solution #2 public 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.

16 Solution #2 public 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 two halves of the equation multiplied by themselves

17 Solution #2 public 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...

18 Solution #2 public 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 is even, so we can just recurse to solve that portion of the equation.

19 Solution #2 public 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.

20 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(log 2 n) For that, you deserve a raise!

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

22 The Cost of Recursion 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 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

23 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 recursive call creates a new stack frame time stack height

24 public int fact ( int num ){ if (num == 0) return 1; else return num * ( fact (num -1) ); } 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

25 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) ); } Problems We 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 example of augmentative (head) recursion. Memory usage grows at O(n). This gets expensive!

26 Analyzing Augmentative Recursion public 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!

27 Tail 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.

28 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 need to save each frame! These values are all precalculated; no need to save each frame!

29 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 overloading takes place of “helper” method Method overloading takes place of “helper” method Need to “prime the pump” with good starting values. Need to “prime the pump” with good starting values. These values are all precalculated; no need to save each frame! These values are all precalculated; no need to save each frame!

30 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! Each recursive call makes calculations independent of the prior recursive calls. There’s no need to save the state of each call 1 1 4

31 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(...);


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

Similar presentations


Ads by Google