Presentation is loading. Please wait.

Presentation is loading. Please wait.

CS2013 LECTURE 5 John Hurley Cal State LA.

Similar presentations


Presentation on theme: "CS2013 LECTURE 5 John Hurley Cal State LA."— Presentation transcript:

1 CS2013 LECTURE 5 John Hurley Cal State LA

2 Recursion Recursion is the technique of breaking down a problem into smaller instances of the same problem. In programming, a recursive algorithm is one that directly or indirectly invokes itself direct recursion: methodA calls methodA indirect recursion: methodA calls methodB and methodB calls methodA

3 Recursion Recursion is used in many common algorithms. It is also extremely important in functional programming, a long-established paradigm (way of thinking about and practicing programming) which is increasingly important today. As you will learn in CS3120, recursion is equivalent to iteration. In other words, any problem you can solve with either of these two techniques, you can also solve with the other. However: a) many problems can be solved with simpler algorithms using recursion, but b) most programmers find iteration easier to think about

4 If you are finished, stop. Otherwise
4 Recursive Algorithm doSomething() If you are finished, stop. Otherwise Solve part of the problem Run this algorithm

5 For example, we can define the operation goHome() this way:
Recursion 5 For example, we can define the operation goHome() this way: Look around to see whether you are at home. If you are, stop. Else Take one step toward home. goHome() 5

6 Recursion Avoid infinite regression (and stack overflows) by defining one or more conditions that indicate the recursion has extended as far as it can, and that therefore cause termination of the recursion. The instance of the problem in which the condition is true is called the base case.

7 © 2014 Goodrich, Tamassia, Goldwasser
Content of a Recursive Method Base case(s) Values of the input variables for which we perform no recursive calls are called base cases (there should be at least one base case). Every possible chain of recursive calls in a correct algorithm must eventually reach a base case. Recursive calls Calls to the current method. Each recursive call should be defined so that it makes progress towards a base case. Recursion © 2014 Goodrich, Tamassia, Goldwasser 7

8 Iterative (not Recursive) Factorial
8 Iterative (not Recursive) Factorial function factorial is: input: integer n such that n >= 0 output: [n × (n-1) × (n-2) × … × 1] Iterative algorithm create new variable called running_total with a value of 1 begin loop if n is 0, exit loop set running_total to (running_total × n) decrement n repeat loop return running_total end

9 9 Recursive Factorial Calculating the factorial of (a positive integer) n can be reduced to multiplying n times the factorial of n-1. function factorial: input: integer n such that n >= 0 output: [n × (n-1) × (n-2) × … × 1] if n == 0, return 1 otherwise, return [ n × factorial(n-1) ] Note that this recursive solution has a simpler relationship to the actual definition of factorial than the iterative solution does

10 Recursive Factorial public class Demo {
10 Recursive Factorial public class Demo { public static long factorialRecursive(int n){ if(n == 0) return 1; return n * factorialRecursive(n -1); } public static void main(String[] args) { int num = Integer.parseInt(JOptionPane.showInputDialog(null, "Please " + "enter the number whose factorial you would like to compute")); JOptionPane.showMessageDialog(null, "The factorial of " + num + " is " + factorialRecursive(num));

11 © 2014 Goodrich, Tamassia, Goldwasser
Visualizing Recursion Recursion trace A box for each recursive call An arrow from each caller to callee An arrow from each callee to caller showing return value Example recursiveFactorial ( 4 ) 3 2 1 return call * = 6 24 final answer Recursion © 2014 Goodrich, Tamassia, Goldwasser 11

12 Stack Overflow Each method call requires additional space on the call stack to track the data values for the current instance of the method If the stack exceeds the memory available to it, a stack overflow occurs public class StackOverflowDemo { int x; public static void main(String[] args){ int x = 1; StackOverflowDemo s = new StackOverflowDemo(); s.recurseToOverflow(x); } public void recurseToOverflow(int x){ System.out.println("instance " + x); x = x + 1; recurseToOverflow(x);

13 Stack Overflow If you get a stack overflow error while doing your work in this class, there is almost certainly a bug in your code that involves an incorrect base case. Think until you figure it out and fix it. However, note that we run our programs inside Eclipse, which decides how much space to allocate to the call stack.

14 Reversing an Array Algorithm reverseArray(A, i, j):
Input: An array A and nonnegative integer indices i and j Output: The reversal of the elements in A starting at index i and ending at if i < j then Swap A[i] and A[ j] reverseArray(A, i + 1, j − 1) return © 2014 Goodrich, Tamassia, Goldwasser Recursion 14

15 Defining Arguments for Recursion
In creating recursive methods, it is important to define the methods in ways that facilitate recursion. This sometimes requires we define additional parameters that are passed to the method. For example, we defined the array reversal method as reverseArray(A, i, j), not reverseArray(A) public class Demo { public static void main(String[] args) { int[] data = {2, 3, 4, 5, 7, 10, 13, 22, 42}; System.out.println("Original array"); for (int i: data) System.out.printf("%d ", i); System.out.println(); reverseArray(data, 0, data.length -1); System.out.println("Reversed array"); } public static void reverseArray(int[] data, int low, int high) { System.out.println ("low = " + low + "; high = " + high); if(low < high) { int temp = data[low]; data[low] = data[high]; data[high] = temp; reverseArray(data, low + 1, high - 1); Recursion 15

16 Binary Search You have seen iterative binary search. Here is a recursive version. package demos; public class BinarySearchDemo { public static boolean binarySearch(int[] data, int key, int low, int high) { System.out.println("Searching for " + key + " in array segment: "); for (int counter = low; counter <= high; counter++) System.out.printf("%d ", data[counter]); System.out.println(); if (low > high) return false; else { int mid = (low + high) / 2; if (key == data[mid]) return true; else if (key < data[mid]) return binarySearch(data, key, low, mid - 1); else return binarySearch(data, key, mid + 1, high); } public static void main(String[] args) { int[] data = { 1, 3, 4, 6, 7, 8, 10, 13, 15, 22, 33, 49 }; int key = 5; System.out.println(binarySearch(data, key, 0, data.length - 1)); © 2014 Goodrich, Tamassia, Goldwasser Recursion 16

17 Visualizing Binary Search
We consider three cases: If the target equals data[mid], then we have found the target. If target < data[mid], then we recur on the first half of the sequence. If target > data[mid], then we recur on the second half of the sequence. © 2014 Goodrich, Tamassia, Goldwasser Recursion 17

18 Multiple Recursion Fibonacci numbers: f(n) =
A recursive problem may have more than one base case and/or more than one recursive call. The definition of the Fibonacci numbers is recursive, and the nth Fibonacci number can be found using a recursive function. Fibonacci numbers: f(n) =

19 Multiple Recursion public class FibonacciCalculator {
public static void main(String[] args){ FibonacciCalculator f = new FibonacciCalculator(); for(int counter = 0; counter < 10; counter++){ long fib = f.fibonacci(counter); System.out.println("Fibonacci number No. " + counter + " = " + fib); } public static long fibonacci(long n) { // if (n < 0) return -1; // F(n) is not defined when n is negative if (n == 0) return 0; else if (n == 1) return 1; else return fibonacci(n-1) + fibonacci(n-2);

20 Analysis Let nk be the number of recursive calls by BinaryFib(k)
n2 = n1 + n0 + 1 = = 3 n3 = n2 + n1 + 1 = = 5 n4 = n3 + n2 + 1 = = 9 n5 = n4 + n3 + 1 = = 15 n6 = n5 + n4 + 1 = = 25 n7 = n6 + n5 + 1 = = 41 n8 = n7 + n6 + 1 = = 67. Note that nk at least doubles every other time That is, nk > 2k/2. It is exponential! Watch what happens as n increases in the code above © 2014 Goodrich, Tamassia, Goldwasser Recursion 20

21 21 Backtracking Some algorithms involve following one path as far as it can go, then backing up to the last point at which a different path could have been chosen and then following that path Depth-First Search (DFS) is a classic example. Start at the root of a tree or graph and explore as far as possible along each branch before backtracking.

22 22 Depth-First Search As we will see in a later lecture, this is a preorder depth-first traversal Source of picture:

23 Recursive Maze Solver package mazedemo;
//adapted from example by Katy Borner, Indiana University // public class MazeSearch { public static void main(String[] args) { Maze labyrinth = new Maze(); if (labyrinth.solve(0, 0)) System.out.println("Maze solved!"); else System.out.println("No solution."); labyrinth.printMaze(); }

24 Recursive Maze Solver package mazedemo; class Maze {
// 0 = wall, 1 = unexplored space, 3 = explored space. At end 3 = explored but not on final path, 7 = on final path int[][] grid = { { 1, 1, 1, 0 }, { 1, 0, 1, 1 }, { 0, 0, 1, 1 } }; public void printMaze() { for (int row = 0; row < grid.length; row++) { for (int column = 0; column < grid[row].length; column++) System.out.print(grid[row][column]); System.out.println(); } public boolean solve(int row, int column) { boolean done = false; if (isValid(row, column)) { System.out.println("curr = " + row + ", " + column); grid[row][column] = 3; // cell has been tried printMaze(); if (row == grid.length - 1 && column == grid[0].length - 1) done = true; // maze is solved else { done = solve(row + 1, column); // down if (!done) done = solve(row, column + 1); // right done = solve(row - 1, column); // up done = solve(row, column - 1); // left if (done) // part of the final path grid[row][column] = 7;

25 Recursive Maze Solver else showWhyInvalid(row, column); return done; }
private void showWhyInvalid(int row, int column) { if (row < 0 || row >= grid.length || column < 0 || column >= grid[0].length) System.out.println(row + ", " + column + " is off the grid\n"); else { System.out.print("square at " + row + ", " + column + " is "); if (grid[row][column] == 0) System.out.print("a wall\n\n"); if (grid[row][column] == 3) System.out.print("already explored\n\n"); private boolean isValid(int row, int column) { boolean result = false; // check if cell is in the bounds of the matrix if (row >= 0 && row < grid.length && column >= 0 && column < grid[0].length) // check if cell is not blocked and not previously tried if (grid[row][column] == 1) result = true; return result;

26 Recursive Maze Solver Desk Check recursive maze solver. Make sure to keep track of which instances of solve() are not on the final path. 1110 1011 0011 Curr = 0,0 3110 Curr = 1,0 Curr = 2, 0 Since we end up backtracking from this instance of solve(), it terminates and will not be on the stack 3110 when the recursion unwinds. However, there is only one maze, so the value of 2,0 remains 3 (explored) 3011 Backtrack! ,0 not on final path Etc

27 27 Towers Of Hanoi The Towers of Hanoi problem supposes that, at the beginning of time, a group of monks in Hanoi were tasked with moving a set of 64 disks of different sizes between three pegs, according to these rules: No disk may ever be placed above a smaller disk The starting position has all the disks, in descending order of size, stacked on the first peg The ending position has all the disks in the same order, stacked on the third peg. An optimal solution for n disks requires 2n-1 moves 264 = 18,446,744,073,709,551,616  There is a nice animation of a 4-disk version of the problem at

28 Towers Of Hanoi Recursive solution:
28 Towers Of Hanoi Recursive solution: For any n > 1, the problem can be solved optimally in this way: Solve the problem for n -1 disks, starting at the start post and ending at the "extra" post. The remaining disk will be the largest one. Move it to the finish post. Then solve the problem for the n-1 disks, moving from the "extra" post to the finish post The above procedure is applied recursively until n = 0 Before you try to understand the code from the book, try out the puzzle online at _hanoi/

29 29 Towers Of Hanoi The key to a Towers Of Hanoi solution is a recursive method like this: public void move(int disks, int from, int to) { if (disks > 0) { int other = 3 - from - to; move(disks - 1, from, other); towers[to].add(towers[from].remove()); move(disks - 1, other, to); }

30 30 Tail Recursion A tail-recursive method is one in which there is one recursive call at the very end of the method, e.g. the examples above of recursive factorial calculations. In many programming languages, compilers can convert tail recursion into iterative code, which is more efficient since it does not create new instances on the call stack and since, in many cases, it can avoid redundant calculations. This is called Tail-Call Optimization, or TCO. Java compilers may have this feature in the future, but not at the current time.

31 Recursive Jokes To understand recursion, read this sentence.
31 Recursive Jokes To understand recursion, read this sentence. A truly greedy man is one who writes his will and names himself as heir -- from Philogelos, the oldest known book of jokes, compiled about 400 AD. Function recurse() If you are done with your program, stop Otherwise, A. Try to find the problem B. Utter an obscenity recurse()

32 32 More on Desk Tracing Do a desk trace of this recursive code, then run the code and see if the actual output matches: package demos; public class RecursiveTracer { final int MAX = 4; public static void main(String[] args) { RecursiveTracer t = new RecursiveTracer(); t.trace(0); } public void trace(int curr) { printStack(curr); if(curr < MAX) trace(++curr); public void printStack(int curr){ for(int counter = curr; counter >= 0; counter--){ System.out.printf(" \n|counter = %2d |\n", counter); System.out.println(" \n\n");


Download ppt "CS2013 LECTURE 5 John Hurley Cal State LA."

Similar presentations


Ads by Google