Recursion Recursion 1 Recursion What kind of problem is “recursive”? Recursive problems are those problems where in order to solve the original problem you have to solve one or more identical but smaller version(s) of the same problem. i.e., To solve problem of “size n”, first solve the of “size n-1 or less”. same problem Examples: • Binary Search • Finding nth Fibonacci number • Finding Factorials How are each of these • Finding exponents • TOH • Finding your way out of maze recursive problems? • 8-queens problem • Graph traversal problems •… Recursion 2 Recursion First understanding a method and method call: … method(int k){ //method implementation //solves problem of size k } For a method, the method call defines the problem size: Solve a problem of “size n” - method(n) Solve a problem of “size n-1” - method(n-1) Solve a problem of “size n/2” - method(n/2) Solve a problem of “size 1” - method(1) Implicit sizes Solve a problem over an array of n elements - method(int[] array, int left, int right); Recursion 3 Recursive problems and methods: Problem: Find the exponent xn Recursive problems are those problems where in order to solve the original problem you have to solve one or more identical but smaller version(s) of the problem until you reach the trivial or base case. Recursive solution 1: Recursive solution 2: xn xn = xn-1 * x1 First solve xn-1 xn/2 * xn/2, if n is even xn/2 * xn/2 * x, if n is odd = To find: xn First solve xn/2 To find: xn … x2 = x1 * x1 x1 = x0 * x1 x0 = 1 Trivial or base case Recursion … x2 = x1 * x1 x1 = x0 * x0 * x1 x0 = 1 4 Recursive problems and methods: Problem: Find the exponent xn Corresponding method signature: /** * purpose of this method is to calculate x^n * @param x - the base * @param n - the exponent, n >= 0 * @return x^n */ public static double power(double x, int n) Usage: Solve 5^3 - power(5,3); Solve 0.031^9 - power(0.031,9); Solve 10472834^0 - power(10472834,0); Solve x^n - power(x,n) Solve x^(n-1) - power(x,n-1) Recursion 5 Recursive problems and methods: Connecting the problem and the code The problem xn = xn-1 * x1 + The method signature /** * purpose of this method is to calculate x^n * @param x - the base * @param n - the exponent, n >= 0 * @return x^n */ public static double power(double x, int n) Solve xn = power(x,n) = power(x,n-1) * x Recursion 6 Recursive problems and methods: Corresponding method: Problem: Find the exponent xn Recursive solution: xn = xn-1 * x1 Solve x^(n-1) /** * purpose of this method is to calculate x^n * @param x - the base * @param n - the exponent, n >= 0 * @return x^n */ public static double power(double x, int n) { if (n == 0) Solve x^n return 1; Base case always first! … x1 = x0 * x1 x2 = x1 * x1 Recursion next x0 = 1 double ans = power(x,n-1) * x; return ans; } Recursion 7 Recursive problems and methods: Trace the following call: … System.out.println(power(5,4)); ... Recursion 8 Recursive problems and methods: Corresponding method: Problem: Find the exponent xn Recursive solution: xn = /** * purpose of this method is to calculate x^n * @param x - the base * @param n - the exponent, n >= 0 * @return x^n */ public static double power(double x, int n) xn/2 * xn/2, if n is even xn/2 * xn/2 * x, if n is odd { if (n == 0) Solve xn/2 double xn; double xno2 = power(x,n/2); if (n%2 == 0) xn = xno2 * xno2; else xn = xno2 * xno2 *x; Solve x^n … x2 = x1 * x1 x1 = x0 * x0 * x1 x0 = 1 return 1; return xn; } Recursion 9 Recursion - Can you do this? Find x! as follows: 0! = 1 x! = x*(x-1)! /** * purpose of this method is to calculate n! * @param n - the value whose factorial we have to find * @return n! */ public static double factorial(int n) Recursion 10 Recursion - another example: Problem: f(n) = 1, when n is 0 or 1 Base case f(n) = f(n-1) + f(n-2) when n > 1 Recursive case /** * Finds the nth Fibonacci number * @param n - the index of the Fibonacci number to find, n >= 0 * @return nth Fibonacci number */ public static int fibonacci(int n){ if ((n==0) || (n==1)) return 1; //apparently, n is not 0 or 1 return fibonacci(n-1) + fibonacci(n-2); } Trace the following: System.out.println(fibonacci(4)); Recursion 11 Recursion: Inherent cost of recursion: A lot of system overhead associated with method calls Advantages of recursion: Provides a clear readable intuitive solution to the problem. Recursion 12 How to problem solve recursively Given a problem of size n ask the following question: “ASSUMING I knew the solution to a smaller but identical sub problem, can I use that answer to solve my current problem?” If “yes”, how do you proceed? 1. Imagine m() to be a method that will solve all such smaller problems. 2. Imagine calling m() and incorporate the solution from m() to solve current problem. [Think carefully about the signature of m().] 3. What size is the smallest sub problems? (Note: smallest sub problem cannot be broken apart because if it could that would imply a still smaller problem!) 4. All such small sub problems found in 3 are your base cases. 5. Replace all m()‘s with a call to your self. 6. Put all the pieces together. Recursion 13 How to problem solve recursively Problem: Write a method to find xn Can this problem be solved recursively? “ASSUMING I knew the solution … 1. Imagine a method m() that will solve this problem, and call it m(x,n-1) -> returns an int, xn-1 2. Imagine calling m() and incorporating that solution to solve … int x_to_n = x * m(x, n-1) 3. What size is the smallest sub problems? n = 0, x0 = 1 4. All such small sub problems found in 3 are your base cases. if (n == 0) return 1; 5. Replace all m()‘s with a call to your self. int x_to_n = x * power(x,n-1); Recursion 14 How to problem solve recursively 6. Put all the pieces together. public static int power(int x, int n){ if (n == 0) return 1; //apparently, n is not zero int x_to_n = x * power(x,n-1); return x_to_n; } Recursion 15 The (classic) Towers of Hanoi problem Objective: To move n rings from any peg start peg (s) to any destination peg (d), without 1. moving more than one ring at a time, and 2. without placing a larger ring on top of a smaller ring. peg 1 peg 2 peg 3 ? s = 1, d = 3 Analysis/algorithm: Is it recursive? What would method m() look like? Can we phrase the solution in terms of m()? Recursion 16 Towers of Hanoi problem /** * The purpose of this method is to solve the towers of Hanoi * problem for n rings being moved amongst 3 pegs, numbered 1,2 * and 3. * @param n - the number of rings to move * @param o - the origination peg (where the rings currently are) * @param d - where the rings should be moved too */ public static void toh(int n, int o, int d){ //check for base if (n == 1) System.out.println(“move a ring from “+o+” to “+d); else{ toh(n-1,o, 6-o-d); System.out.println(“move a ring from “+o+” to “+d); toh(n-1, 6-o-d, d); } } Recursion 17 Towers of Hanoi problem Trace: toh(3,1,3) TOH(3,1,3) TOH(2,1,2) TOH(1,1,3) TOH(2,2,3) TOH(1,3,2) TOH(1,2,1) TOH(1,1,3) What is the performance of toh()? Recursion 18 Recursion and arrays - print an array out, backwards Problem: print an array backwards. Can this problem be solved recursively? 1. Imagine a method m() that will solve this problem. [Think carefully about the signature of such a method.] 2. Imagine calling m() and incorporating that solution to solve … 3. What size is the smallest sub problems? 4. All such small sub problems found in 3 are your base cases. 5. Replace all m()‘s with a call to your self. [What happens if the signatures don’t match?] Performance? 6. Put all the pieces together. Recursion 19 Searching an array Problem: Search and array, A, of integers for a value B. If found return location index; otherwise return –1. Performance? Recursion 20 Recursion - Divide and Conquer - Binary search /** * This method searches an array of sorted Comparable objects * @param a - the array of sorted Comparable objects * @val - the value searched for * @l - the left starting index * @r - the right starting index * @return array index of value if found; -1 otherwise. */ public static int binarySearch(int[] a, int val, int l, int r){ if (l > int mid if (val if (val else r) = (l+r)/2; == a[mid]) < a[mid]) return -1; //not found return mid; //found return binarySearch(a,val,l,mid-1); return binarySearch(a,val,mid+1,r); } Performance? Recursion 21 Sorting an array Problem: Sort an integer array, A. What recursive strategies can we use, if any? Can you come up with any? Recursion 22 Recursion - Divide and Conquer - Merge Sort /** This method will sort an array using merge sort*/ public static int[] sort(int a[]){ return mergeSort(a, l, r); } /** This method will sort an array of Comparable objects * using Merge sort * @param array - the array to sort * @param l - the left index * @param r - the right index */ public static void mergeSort(int[] array, int l, int r){ if (l < r){ int mid = (l+r)/2; //divide in half mergeSort(array, l, mid); //sort each half mergeSort(array, mid+1, r); merge(array, l,mid,r); //merge sorted halves } } public static void merge(int[] 1. create a temp array to 2. merge the two halves 3. Make sure all elements 4. Copy elements from the } array, int l, int mid, int r){ hold the merging halves of the two halves are in the temp array temp array back to array. Performance? Recursion 23 Recursion - Divide and Conquer - Merge Sort public static void merge(int[] array, int l, int mid, int r){ //create an array int[] temp = new int[r+1]; //merge int li=l, ri = mid+1, ti = l-1; while ((li <= mid) && (ri <= r)){ ti++; if (array[li] < array[ri]){ temp[ti] = array[li]; li++; } else{ temp[ti] = array[ri]; ri++; } } // Make sure all elements of the two halves are in the temp array for(int i=li; i<=mid; i++, ti++) temp[ti] = array[i]; for(int i=ri; i<=r; i++, ti++) temp[ti] = array[i]; //Copy elements from the temp array back to array. for(int i=l; i<=r; i++) array[i] = temp[i]; } Recursion 24 Recursion - Divide and Conquer - Quick Sort Problem: Sort an integer array, A. public static int[] sort(int[] a) Can this problem be solved recursively? Imagine 1: Move elements in array into the left and right of the array such that all the elements on the left are less than some pivot value and all the elements to the right are greater. Imagine that you know this break even index. Imagine 2: Imagine that m() is a method that will sort an array’s elements between l and r: int[] m(int[] a, int l, int r) 2. Imagine calling m() and incorporating that solution to solve … int pivIndex = partition(a, l,r); m(a,l,pivIndex-1); m(a,pivIndex+1, r); 3. Base case(s) if (l < r) { Recursion 25 Recursion - Divide and Conquer - Quick Sort /** This method will sort an array using quick sort*/ public static int[] sort(int a[]){ return quiskSort(a, l, r); } /** This method will sort an array of Comparable objects * using Quick sort * @param array - the array to sort * @param l - the left index * @param r - the right index */ private static void quickSort(int[] array, int l, int r){ if (l < r){ int pivot = partition(array,l,r); quickSort(array, l, pivot-1); //sort each half quickSort(array, pivot+1, r); } } private static void partition(int[] array, int l,int r){ 0. Pivot value is array[l] 1. Scan from left to right and right to left moving items larger than pivot to the right and those smaller than pivot to the left 2. Move pivot to its sorted position Performance? 3. return pivot index } Recursion 26 Recursion - Divide and Conquer - Quick Sort public static void partition(int[] array, int l,int r){ //Pivot value is array[l] int pivotVal = array[l]; //Scan from left to right and right to left moving items larger //than pivot to the right and those smaller than pivot to the left int li = l, ri = r; while (li < ri){ while ((pivotVal > array[li]) && (li<ri)) li++; while (pivotVal < array[ri]) ri--; if (li<ri){ int temp = array[li]; array[li] = array[ri]; array[ri] = temp; li++; ri--; } } //Move pivot to its sorted position array[l] = array[ri]; array[ri] = pivotVal; //return pivot index return ri; } Recursion 27