400 likes | 583 Views
Stuff. No stuff today!. Last Time. The complexity of binary search. Started Recursion by looking at what recursion is, and how the java runtime engine uses activation frames to handle program flow. Today. Recursion, Cont. Types of recursion: Tail Non-Tail Indirect Excessive Infinite
E N D
Stuff • No stuff today! CISC121 - Prof. McLeod
Last Time • The complexity of binary search. • Started Recursion by looking at what recursion is, and how the java runtime engine uses activation frames to handle program flow. CISC121 - Prof. McLeod
Today • Recursion, Cont. • Types of recursion: • Tail • Non-Tail • Indirect • Excessive • Infinite • Backtracking CISC121 - Prof. McLeod
Recursion and the Run-Time Stack – Summary • Execution stops at any method call and execution is passed to that method. • Information on the calling method is preserved in the run-time stack, and a new frame is created for the newly called method. • As recursive calls continue, the frames continue to pile up, each one waiting to complete their execution. • Finally, the last recursive call is made and the “anchor condition” or the “stopping case” is encountered. The method completes without making another recursive call and may provide a Return value, for example. CISC121 - Prof. McLeod
Recursion and the Run-Time Stack – Summary, Cont. • The frame for the last call, the “stopping case” is popped off the stack, with the Return value being passed down into the next frame. • The frame below accepts the Return value, completes, gets popped off, and so on, until the only frame remaining is the frame that contains the original call to the recursive method. • This frame accepts the value from the method, and continues its execution. CISC121 - Prof. McLeod
An Example • Remember that the activation frames can also hold code after the recursive call. • See TestRecursion.java CISC121 - Prof. McLeod
Another Example • Write code that computes the sum below for n terms: • (This sum does not converge very quickly!) CISC121 - Prof. McLeod
Another Example – Cont. • Write sum using a recursive definition: CISC121 - Prof. McLeod
Tail Recursion public static double sum (int n) { if (n == 1) return 1.0; else return Math.pow(-1, n) / n + sum(n-1); } // end sum • (Note that Math.pow() returns a double). • This kind of recursion is called “tail recursion”, since the recursive call is at the end of the method. CISC121 - Prof. McLeod
Tail Recursion – Cont. • Tail recursions can always be easily written non-recursively with a loop: public static double loopsum (int n) { double sum = 1.0; for (int i = 2; i <= n; i++) sum += Math.pow(-1, i) / i; return sum; } // end loopsum CISC121 - Prof. McLeod
Comparison of Recursion and Loops • Using “System.currentTimeMillis()”. • Results (times are average of four readings): CISC121 - Prof. McLeod
Tail Recursion – Cont. • So, this experiment shows that the loop is a bit faster than recursion and will not encounter the stack overflow condition. • There is usually little reason to write a recursive method that uses only tail recursion - use a loop instead. • Tail recursion is when the recursive call is at the end of the method, and there is only one recursive call. CISC121 - Prof. McLeod
Non-Tail Recursion • Another example, a method to calculate the “Greatest Common Divisor”, or “GCD” for two integers. • For example, GCD(800, 64) would return 32. CISC121 - Prof. McLeod
Non-Tail Recursion – Cont. • In code: public static int GCD (int n, int m) { if (n < m) return GCD(m, n); else if ((n % m) > 0) return GCD(m, n % m); else return m; } // end GCD CISC121 - Prof. McLeod
Non-Tail Recursion – Cont. • This is a (pretty trivial!) example of “Non-Tail Recursion”, since it has two recursive calls before the end of the method. • (Note that, in Java, unless you have a return statement in each branch of a conditional, you must have a return statement outside the conditional.) • This example could also be coded fairly easily with a loop. CISC121 - Prof. McLeod
Non-Tail Recursion • A method where there are either multiple recursive calls, or the recursive call is not at the end of the method. • It is the most common kind of recursion. • (Quicksort is an excellent example of Non-Tail Recursion). CISC121 - Prof. McLeod
Indirect Recursion • Recursion that does not occur by a direct call. • For example, f() calls g() and then g() calls f(). • Another example used to calculate sin, tan and cos, using the formulae: CISC121 - Prof. McLeod
Indirect Recursion - Cont. • The stopping case for this recursion is for when x is small: • See the program “RecursiveTrigFunctions.java” CISC121 - Prof. McLeod
Excessive Recursion • An example can be made from the definition of the Fibonacci numbers: • The sequence obtained (by adding the two previous numbers to get the next one) is: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, … CISC121 - Prof. McLeod
Excessive Recursion - Cont. • In Java: public int Fib (int n) { if (n < 2) return n; else return Fib(n-2) + Fib(n-1); } // end Fib CISC121 - Prof. McLeod
Excessive Recursion - Cont. • 25 method calls, and 12 additions are required just to calculate Fib(6)!: CISC121 - Prof. McLeod
Excessive Recursion - Cont. • For Fib(30), 2,692,537 calls would be required and 1,346,268 additions!!! • (In testing the code, Fib(40) was taking a very long time, and would likely lead to stack overflow.) • In comparison, an iterative version of the method, that is about three times as long (in code), would only require 87 assignments and 29 additions to calculate Fib(30). CISC121 - Prof. McLeod
Infinite Recursion • This is what happens when the stopping case is not encountered. • For example, if the line if (n == 1) return 1.0; had been left out of the “sum” example, the recursive process would never have hit the stopping case and would have continued until stack overflow occurs (even for small n). CISC121 - Prof. McLeod
Infinite Recursion - Cont. • A “stack overflow” error would have been reported and the program would crash. • The heart of a recursive method contains a conditional statement where at least one branch of the conditional leads to a non-recursive completion. The conditional must test an expression that includes at least one of the method’s parameters. • The recursive calls from the other branches should be made to “smaller” executions of the method. CISC121 - Prof. McLeod
Example of Recursion - Towers of Hanoi A B C • Move all disks from pin A to pin C subject to: • One disk at a time must be moved to a pin from the top. • Cannot have a larger disk over a smaller one. • See powerpoint “solution”… CISC121 - Prof. McLeod
Towers of Hanoi • For n disks: • Apply the solution for n-1 disks, moving them to pin B, using pin C as a temporary pin. • Apply the n=1 solution, moving the largest disk from pin A to pin C. • Apply the n-1 solution again moving disks from pin B to pin C, using pin A as a temporary pin. CISC121 - Prof. McLeod
Towers of Hanoi, Cont. • So, the solution for n disks uses the solution for n-1 disks. • Solution algorithm: • Move n-1 disks from pin A to pin B, using pin C for temporary storage. • Move final disk from pin A to pin C. • Move n-1 disks from pin B to pin C using pin A for temporary storage. • Recursive anchor case: • If n=1 move disk from pin A to pin C. • See TowersSolution.java CISC121 - Prof. McLeod
Backtracking • Backtracking is an algorithmic tool that is used in developing artificial intelligence algorithms. • It is used when different possible paths exist from a given position, and it is not known which one is optimum. • A path is tried, followed by another try, and so on. If the optimum solution is not encountered, then the last path is “backtracked” and another path is tried. The backtracking can go all the way back to the first decision. CISC121 - Prof. McLeod
Backtracking - Cont. BAD BAD CISC121 - Prof. McLeod
Backtracking – Cont. • Three possible outcomes from backtracking: • Stop when the first path to success is found. • Discover all possible paths to success (full exploration). • Discover optimal path (still requires full exploration). • Optimal could be shortest, lowest cost, least effort, etc. CISC121 - Prof. McLeod
Backtracking and Recursion • As usual, recursion makes coding easier: • Recursive backtracking algorithm for single outcome: • If successful, finished! (anchor case) • else: • for every possible, untried choice from current position: • move to the new choice • check to see if the rest of the problem can be solved: (recursive) • if so, finished! • if not, try another choice • if all choices tried without success, backtrack CISC121 - Prof. McLeod
Backtracking - Cont. • For example, the problem of “placing n queens on an n x n chessboard, without any queen threatening any other.” (the “Nqueens” problem) can be solved by backtracking. CISC121 - Prof. McLeod
The “NQueen’s” Problem • One Queen on an 8x8 grid (a chessboard!), and who she “threatens”: CISC121 - Prof. McLeod
The “NQueen’s” Problem - Cont. • The solution for 8 Queen’s on an 8x8 grid, for example: CISC121 - Prof. McLeod
The “NQueen’s” Problem - Cont. • It is easy enough to manually solve this problem for n=4. • Showing each step - see “FourQueensSoln.ppt”. • Watch for backtracking! CISC121 - Prof. McLeod
The “NQueen’s” Problem - Cont. • Number rows and columns from zero. • Move through grid column by column. • Note that only one Queen can occupy each column and therefore each column must have a Queen. • Start from column zero and go to column n-1. CISC121 - Prof. McLeod
The “NQueen’s” Problem - Cont. • Pseudocode for recursive method: boolean solveNQ (int col) if col >= size then all done! for row 0 to row n-1, where n is board size if (row, col) is a safe position: place Queen at (row, col) if solveNQ (col + 1) is true then return true else remove Queen from (row, col) (Outside of loop:) return false CISC121 - Prof. McLeod
static boolean solveNQ(Board B, int col) { // anchor case: we have succeeded! if (col >= B.getSize()) return true; // try putting a queen in each row of this column for (int row = 0; row < B.getSize(); row++) { if (B.safePosition(row, col)) { B.putQueen(row, col); if (solveNQ(B, col+1)) return true; else B.removeQueen(row, col); } // end if } // end for // anchor case: there is no solution return false; } // end solveNQ CISC121 - Prof. McLeod
The “NQueen’s” Problem - Cont. • See “Board.java” and “NQueen.java” for the other methods. CISC121 - Prof. McLeod
Recursion - A Summary • Recursive methods call themselves. • Recursive algorithms are very easy to code when a logical recursive definition (including an anchor case) is given. • Make sure the recursive method includes the anchor case, and calls are made to smaller values each time. • Most recursive algorithms can be coded as an iterative version (using loops). But the iterative version is not always as easy to build (except for tail recursion). • Recursive code is easy to read and de-bug. • It should be used when the longer execution time is not as important as being able to use a smaller program that can be coded and de-bugged in less time. CISC121 - Prof. McLeod