600 likes | 616 Views
Chapter 6. Recursion. Solving simple problems. Iteration can be replaced by a recursive function Recursion is the process of a function calling itself. Computing 2^5th without recursion. #include <iostream.h> int twoRaisedTo0() { return 1; }
E N D
Chapter 6 Recursion
Solving simple problems • Iteration can be replaced by a recursive function • Recursion is the process of a function calling itself
Computing 2^5th without recursion #include <iostream.h> int twoRaisedTo0() { return 1; } int twoRaisedTo1() { return 2 * twoRaisedTo0(); } int twoRaisedTo2() { return 2 * twoRaisedTo1(); } int twoRaisedTo3() { return 2 * twoRaisedTo2(); } int twoRaisedTo4() { return 2 * twoRaisedTo3(); } int twoRaisedTo5() { return 2 * twoRaisedTo4(); }
Main program int main() { cout << "2 to the 5th is " << twoRaisedTo5(); cout << endl; return 0; }
Recursively computing powers of 2 #include <iostream.h> int twoRaisedTo(int n) { if (n == 0) // special case return 1; else return 2 * twoRaisedTo(n-1); }
Main program int main() { cout << "2 to the 5th is " << twoRaisedTo(5); cout << endl; return 0; }
The Nature of Recursion • Solving a problem by first solving a smaller version of it • Recursive solutions have two parts • A recursive call • A recursive stop • The function calls itself until the stop condition is hit, then it returns back through the recursive calls.
Powers of 2 equation When n >= 1 When n = 0
Recursive substitution • It is easy to turn a recursive definition into a recursive program • Keep substituting values until you get an expression that can be evaluated without recursion
Back substitution • Once the recursive stop has been located you can use the value obtained and return to the previous definition. • You then repeatedly work your way back to the place where you started • This is called ‘back substitution’
Factorial problem • This is a classic problem demonstrating the use of recursion • The only problem comes when you try to implement it with ints or long integers (a small integer may generate a factorial that exceeds the representational capacity of the machine.
Equation 6-5 Equation 6-6 When n >= 1 When n = 0
Factorial function int factorial(int n) { // precondition: n is not negative if (n == 0) return 1; else return (n*factorial(n-1); // postcondition: n is not changed // returns: n! // limitations: INT_MAX }
Combinatorial problem • How many different one hour shows can be produced from 40 records if you can only play 10 songs in an hour? • How many combinations of 40 can be arrived at choosing 10 at a time? • “n choose k” problem
Factorial solution • n choose k = n!/(k!(n-k)!) • This problem has a recursive solution that employs our recursive factorial function (nested recursion) • This function has two recursive stops
Another solution • We can also come up with a recursive version of the ‘n choose k’ problem on our own, without factorial.
Equation 6-11 when k = 1 when n = k when n > k and k > 1 Equation 6-12
n choose k function int choose(int n, int k) { if (k == 1) return n; else if (n == k) return 1; else // recursive case: n>k and k>1 return choose(n - 1, k - 1) + choose(n - 1, k); }
Recursion in searching and sorting • Any iterative process (a loop) that terminates can be redefined recursively • Iterative processes are used in both searching and sorting routines
A recursive linear search • Given an array ‘a’, indexed from 0 to n-1 • Function search is called like this: • search(a,n-1,target) where the value n-1 becomes the value for n in the function • Here is it’s recursive algorithm • If a[n-1] == target return n-1 • else • return the result of search(a,n-1,target)
Visualization of recursive Linear Search use recursive call linearSearch(a, n-1, target) for this part a[0] through a[n-2] a[n-1]
Recursive Linear Search int linearSearch(int a[], int n, int target) { // Recursive version of linear search // Precondition: a is an indexed from 0 to n-1 if (n < 0) // an empty list is specified return -1; else {
Recursive linear search (con’t) if (a[n-1] == target) // test final position return n-1; else // search the rest of the list recursively return linearSearch(a, n-1, target); } // Postcondition: If a value between 0 and n-1 is returned, // a[returnValue] == target; // Otherwise, if -1 is returned, target is not in a }
Recursive binary search Preconditions: a is an array sorted in ascending order, first is the index of the first element to search, last is the index of the last element to search, target is the item to search for.
Recursive binary search • The repetitive process involves dividing the search domain in half • The recursive stop happens when the target value equals the middle value of the current list, or when you run out of places to look
Algorithm for binary search • If first > last return -1 (target not found) • If a[mid] == target return mid • else if target < a[mid] • bsearch(a,first,mid-1, target) • else • bsearch(a, mid+1, last, target)
Binary search (con’t) If first > last return failure mid = (first+last)/2 if a[mid] is equal to target return mid else if target < a[mid] return result of recursive search of a from first up to mid-1 else return result of recursive search of a from mid+1 up to last
Recursive binary search code int binarySearch(int a[], int first, int last, int target) { // Preconditions: a is an array sorted in ascending order, // first is the index of the first element to search, // last is the index of the last element to search, // target is the item to search for.
Binary search code if (first > last) return -1; // -1 indicates failure of search int mid = (first+last)/2; if (a[mid] == target) return mid; else if (target < a[mid]) return binarySearch(a, first, mid-1, target); else // target must be > a[mid] return binarySearch(a, mid+1, last, target); // Postcondition: Value returned is position of target in a, // otherwise -1 is returned }
Correctness • Proof of correctness was done with loop invariants for non-recursive loops • Recursive algorithms are proved by induction • Does the program work for the base case? • Does the program work for each case smaller than the current n? • If both are true we assume it has been proved correct.
Recursive quicksort • Quicksort was published in 1962 by British mathematician C.A.R. Hoare • Basic idea: • Pick one item from an array (the pivot) • Reorganize the array so that smaller things are on one side and larger things on the other (partitioning). • Recursively select pivots for each half of the list, partition, select pivots, partition, etc.
Quicksort: The array after one partition partition 1: all items <= pivot pivot partition 2: all items > pivot
Quicksort code int partition(int a[], int first, int last); void quicksort(int a[], int first, int last) { // precondition: a is an array; // The portion to be sorted runs from // index first to index last inclusive. if (first >= last) // Base Case -- nothing to sort, so return return;
Code Example // Otherwise, we’re in the recursive case. // The partition function uses the item in a[first] as the pivot // and returns the position of the pivot -- split -- after the partition. int split(partition(a, first, last)); // Recursively, sort the two partitions. quicksort(a, first, split-1); quicksort(a, split+1, last); // postcondition: a is sorted in ascending order // between first and last inclusive. }
First attempt at a loop invariant for partition first split last partition 1: all items <= pivot partition 2: all items > pivot items yet to be processed 14 9 22 11 4 8 27 41 56 31 33 101 66 14 53 99 11 2 24 87 33 47 22 The initial value ‘27’ is moving right as array elements are processed. This is unnecessarily complicated. A better way would be to keep it in position 0 until all elements have been moved, then swap it with the last small one.
Better loop invariant for partition first lastSmall i partition 1: all items <= pivot partition 2: all items > pivot items yet to be processed 27 14 9 22 11 4 8 41 56 31 33 101 66 14 53 99 11 2 24 87 33 47 22 14 is less than 27, so lastSmall increments by one to point to 41 and then 14 and 41 are switched. Then i moves on to the value 53. 27 14 9 22 11 4 8 14 56 31 33 101 66 41 53 99 11 2 24 87 33 47 22
Getting the pivot into its proper location first lastSmall Exchange a[first] with a[lastSmall]
Partition function void swapElements(int a[], int first, int last); int partition(int a[], int first, int last) { int lastSmall(first), i; for (i=first+1; i <= last; i++) // loop invariant: a[first+1]...a[lastSmall] <= a[first] && // a[lastSmall+1]...a[i-1] > a[first] if (a[i] <= a[first]) { // key comparison ++lastSmall; swapElements(a, lastSmall, i); }
Partition continued // put pivot into correct position swapElements(a, first, lastSmall); // postcondition: a[first]...a[lastSmall-1] <= a[lastSmall] && // a[lastSmall+1]...a[last] > a[lastSmall] return lastSmall; // this is the final position of the pivot -- the split index }
Example recursion tree for Quicksort [67, 58, 38, 81, 90, 57, 54] [54, 58, 38, 57, 67, 81, 90] [54, 58, 38, 57] [81, 90] [38, 54, 58, 57] [81, 90] [38] [58, 57] [] [90] [57, 58] [] [58]
A worst case recursion tree for Quicksort [1,2,3,4,5,6,7] [2,3,4,5,6,7] [] [3,4,5,6,7] [] [] [4,5,6,7] [] [5,6,7] [] [6,7] [7] []
Quicksort versus bubble sort n(as power of 2) n Quicksort Bubble Sort Comparisionsa Comparisons
A best case recursion tree for Quicksort [4, 1, 3, 2, 6, 5, 7] [2, 1, 3, 4, 6, 5, 7] [2, 1, 3] [6, 5, 7] [1, 2, 3] [5, 6, 7] [1] [3] [5] [7]
the number of items to sort at each level The number of levels (I) is no more than log(base 2)n
A uniformly distributed pivot splits near the middle half the time n n 3n 0 n 4 2 4 about half the time, the pivot falls in the shaded area around the middle
How is recursion implemented? • When a function calls itself • The original stops executing and it’s values are stored on the ‘data stack’ • Eventually, the recursive call will return and at that time the values in storage are popped off the stack and the function picks up processing
Example • Given a function b(int x) that contains a call to another function a(int z) within it... • Function b must be temporarily suspended until a finishes. • This means that b, and the values of its variables must be stored on the stack temporarily.