Presentation is loading. Please wait.

Presentation is loading. Please wait.

Summer 2007CISC121 - Prof. McLeod1 CISC121 – Lecture 10 Last time: –The Analysis of Complexity From now on, we will always want to know the complexity.

Similar presentations


Presentation on theme: "Summer 2007CISC121 - Prof. McLeod1 CISC121 – Lecture 10 Last time: –The Analysis of Complexity From now on, we will always want to know the complexity."— Presentation transcript:

1 Summer 2007CISC121 - Prof. McLeod1 CISC121 – Lecture 10 Last time: –The Analysis of Complexity From now on, we will always want to know the complexity of the algorithms we are discussing.

2 Summer 2007CISC121 - Prof. McLeod2 You Will Need To: Work on exercise 4 and assignment 4. Assn 4 is due Saturday at 5pm. If you hand it in on Friday instead, Krista will have it marked earlier.

3 Summer 2007CISC121 - Prof. McLeod3 Today Recursion Coming up – Analyzing the Complexity of Recursive Methods.

4 Summer 2007CISC121 - Prof. McLeod4 Recursion An advanced programming technique that can be used with any language that supports procedure or function calls (or “methods!”). Advanced sorting algorithms, like Quicksort, use recursion. We will also look at how to analyze the complexity of recursive algorithms.

5 Summer 2007CISC121 - Prof. McLeod5 Recursion, Cont. Most simply defined as when a method “calls itself” as part of its normal operation. For example: public int factorial (int n) { if (n == 0) return 1; else return n * factorial(n – 1); } // end factorial method

6 Summer 2007CISC121 - Prof. McLeod6 Recursion, Cont. Start by considering recursive definitions: A recursive definition has two parts: –The “anchor”, “ground case” or “stopping case” supplies all the basic elements out of which all other elements of the set are constructed. This part is not recursive. –The second part consists of the rules that determine how all other elements are constructed. It will define a new element starting from an element that has already been constructed (the “recursive” part).

7 Summer 2007CISC121 - Prof. McLeod7 Recursive Definitions For example, the factorial function, n!, is evaluated as: n! = n(n-1)(n-2)(n-3)…(2)(1) So 15! = 15(14)(13)(12)(11)… = 1307674368000. Or, in terms of a mathematical definition:

8 Summer 2007CISC121 - Prof. McLeod8 Recursive Definitions - Cont. Or, in terms of a recursive definition: The upper part is the anchor, and the lower part is the definition of the rules that are used to get all other values. It is also called the “inductive step”. The lower part is recursive.

9 Summer 2007CISC121 - Prof. McLeod9 Coding a Recursive Definition In code: public int factorial (int n) { if (n == 0) return 1; else return n * factorial(n – 1); } // end factorial method

10 Summer 2007CISC121 - Prof. McLeod10 Coding a Recursive Definition – Cont. “But, wait!” you say! Can’t this just be done in a loop? Yes, most recursive codes can be carried out using loop(s) instead. However, the “looping” version is seldom as compact as the recursive one. Recursion is used in a program when it produces code that is shorter and more easily understood (and debugged!) than code using a loop. Recursive versions are usually slower than looping versions.

11 Summer 2007CISC121 - Prof. McLeod11 Coding a Recursive Definition – Cont. A non-recursive factorial method: public int factorial (int n) { int f = 1; if (n > 0) for (int i = 2; i <= n; i++) f *= i; return f; } // end factorial method

12 Summer 2007CISC121 - Prof. McLeod12 Coding a Recursive Definition – Cont. (Note that we are ignoring the very real possibility of numeric overflow in these factorial methods. It would be better to use a long, or even an “ BigInt ” variable.)

13 Summer 2007CISC121 - Prof. McLeod13 Coding a Recursive Definition – Cont. Back to the recursive factorial method: public int factorial (int n) { if (n == 0) return 1; else return n * factorial(n – 1); } // end factorial method This is a slightly more “elegant” piece of code, and it is easy to write and debug. What is not so easy to understand is how it works!

14 Summer 2007CISC121 - Prof. McLeod14 Back to Stacks - Activation Frames Stacks are an integral part of computer architecture. For example, Java byte code is run by the “Java Virtual Machine”, or “JVM”. The JVM is stack – based. Each thread (or process…) in the JVM has its own private run-time stack in memory (our programs are “single threaded”). Each run-time stack contains “activation frames” – one for each method that has been activated. Only one activation frame (or method) can be active at a time for each thread. An activation frame is created, or “pushed”, every time a method is invoked. When the method is completed, the frame is popped off the stack.

15 Summer 2007CISC121 - Prof. McLeod15 Recursion and the Run-Time Stack Consider another simple recursive definition (ignore n<0): public static double power (double x, int n) { if (n == 0) return 1.0; else return x * power(x, n-1); } // end power

16 Summer 2007CISC121 - Prof. McLeod16 Recursion and the Run-Time Stack – Cont. In a main method: public static void main(String[] args) { double z = power(5.6, 2); } // end main Look at what happens in debug mode in Eclipse…

17 Summer 2007CISC121 - Prof. McLeod17 An activation frame for this first call to power is pushed onto the run-time stack, and execution is transferred to this frame (execution does not continue in main). The power method runs until it hits the second call to power on line: power(5.6, 1) // second call Recursion and the Run-Time Stack – Cont. main power 5.6, 2

18 Summer 2007CISC121 - Prof. McLeod18 Execution in the first call frame stops and a frame for the second call is pushed onto the stack. Execution in the second call frame continues until it hits the third call to power : power(5.6, 0) // third call Recursion and the Run-Time Stack – Cont. main power 5.6, 1 power

19 Summer 2007CISC121 - Prof. McLeod19 Execution in the second call frame stops and a frame for the third call is pushed onto the stack. Now, power executes until it reaches the line: This causes the value 1.0 to be placed at the “Return value” part of the third call’s activation frame. Recursion and the Run-Time Stack – Cont. main power 5.6, 0 power return 1.0;

20 Summer 2007CISC121 - Prof. McLeod20 This third call to power completes, and the third call’s frame is popped off the stack. The second call’s frame accepts the value of 1.0, and execution resumes when 5.6 * 1.0 is calculated. Recursion and the Run-Time Stack – Cont. main power 1.0

21 Summer 2007CISC121 - Prof. McLeod21 This allows the second call’s frame to complete execution. The resulting value (5.6) is placed in the second call’s Return value part of the frame. Recursion and the Run-Time Stack – Cont. main power 5.6

22 Summer 2007CISC121 - Prof. McLeod22 Recursion and the Run-Time Stack – Cont. The second call’s frame is popped off the stack, and the value “5.6” is given to the first call’s frame and execution resumes at line when 5.6 * 5.6 is calculated. The first call’s execution completes and the first call’s frame is popped off the stack providing the value “31.36” to the next frame on the stack. main power 31.36

23 Summer 2007CISC121 - Prof. McLeod23 Recursion and the Run-Time Stack – Cont. Execution resumes in the main frame, where the value “31.36” is put into the variable z. See the factorial method, as well (when does the factorial calculation actually take place?). main y=31.36

24 Summer 2007CISC121 - Prof. McLeod24 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.

25 Summer 2007CISC121 - Prof. McLeod25 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.

26 Summer 2007CISC121 - Prof. McLeod26 Another Example Remember that the activation frames can also hold code after the recursive call. See TestRecursion.java

27 Summer 2007CISC121 - Prof. McLeod27 Another Another Example Write code that computes the sum below for n terms: (This sum does not converge very quickly!)

28 Summer 2007CISC121 - Prof. McLeod28 As a Recursive Definition The top line is the anchor case, and the bottom part contains the rules for all members of the set.

29 Summer 2007CISC121 - Prof. McLeod29 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.

30 Summer 2007CISC121 - Prof. McLeod30 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

31 Summer 2007CISC121 - Prof. McLeod31 Comparison of Recursion and Loops Using “ System.currentTimeMillis() ”. Results (times are average of four readings): # termsrecursion (msec) loop (msec) 22507060 7080696613 2237083056041 70720Stack Overflow!60718

32 Summer 2007CISC121 - Prof. McLeod32 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.

33 Summer 2007CISC121 - Prof. McLeod33 Non-Tail Recursion Example: A recursive algorithm to calculate the “Greatest Common Divisor”, or “GCD” for two integers. For example, GCD(800, 64) would return 32.

34 Summer 2007CISC121 - Prof. McLeod34 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

35 Summer 2007CISC121 - Prof. McLeod35 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.

36 Summer 2007CISC121 - Prof. McLeod36 Non-Tail Recursion, Defined 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) (We’ll get there – be patient!).

37 Summer 2007CISC121 - Prof. McLeod37 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:

38 Summer 2007CISC121 - Prof. McLeod38 Indirect Recursion - Cont. The stopping case for this recursion is for when x is small: See the program “RecursiveTrigFunctions.java”

39 Summer 2007CISC121 - Prof. McLeod39 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, …

40 Summer 2007CISC121 - Prof. McLeod40 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

41 Summer 2007CISC121 - Prof. McLeod41 Excessive Recursion - Cont. 25 method calls, and 12 additions are required just to calculate Fib(6)!:

42 Summer 2007CISC121 - Prof. McLeod42 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).

43 Summer 2007CISC121 - Prof. McLeod43 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). Infinite Recursion

44 Summer 2007CISC121 - Prof. McLeod44 Example of Recursion - Towers of Hanoi 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”… ABC

45 Summer 2007CISC121 - Prof. McLeod45 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.

46 Summer 2007CISC121 - Prof. McLeod46 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

47 Summer 2007CISC121 - Prof. McLeod47 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.

48 Summer 2007CISC121 - Prof. McLeod48 Backtracking - Cont. BAD

49 Summer 2007CISC121 - Prof. McLeod49 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.

50 Summer 2007CISC121 - Prof. McLeod50 Backtracking and Recursion As usual, recursion makes coding easier: Recursive backtracking algorithm for single outcome: –If successful, finished! (anchor case) –else: if there are no open choices from current position backtrack 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 from this position have been tried without success, backtrack

51 Summer 2007CISC121 - Prof. McLeod51 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. Note that we will discuss how to locate the first solution.

52 Summer 2007CISC121 - Prof. McLeod52 The “NQueen’s” Problem One Queen on an 8x8 grid (a chessboard!), and who she “threatens”:

53 Summer 2007CISC121 - Prof. McLeod53 The “NQueen’s” Problem - Cont. The solution for 8 Queen’s on an 8x8 grid, for example:

54 Summer 2007CISC121 - Prof. McLeod54 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!

55 Summer 2007CISC121 - Prof. McLeod55 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.

56 Summer 2007CISC121 - Prof. McLeod56 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

57 Summer 2007CISC121 - Prof. McLeod57 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

58 Summer 2007CISC121 - Prof. McLeod58 Recursive Sorts We will look at: –Mergesort –Quicksort

59 Summer 2007CISC121 - Prof. McLeod59 Mergesort The one problem, that we will see later, with Quicksort is that it risks being very slow (O(n 2 )) if appropriate pivot values cannot be chosen. (It would be the same as bubble sort!) Mergesort simply divides the array right down the middle, sorts both halves and then merges the halves back together. Mergesort does not care about the state of the data, where Quicksort can be very sensitive to this. Mergesort is very easy to code with recursion.

60 Summer 2007CISC121 - Prof. McLeod60 Mergesort - Cont. Pseudocode: mergesort(A, first, last) –If first < last mid = (first + last) / 2 mergesort(A, first, mid) mergesort(A, mid+1, last) merge(A, first, last) How can this algorithm be so fast?

61 Summer 2007CISC121 - Prof. McLeod61 Mergesort - Cont. Pseudocode for merge algorithm: merge (A, first, last) –mid = (first + last) / 2 –i = 0; j = first; k = mid + 1 –While both left and right subarrays contain elements: If A[j] < A[k] –temp(i++) = A(j++) Else temp[i++] = A[k++] –Load remaining elements from A into temp. –Load temp back into A.

62 Summer 2007CISC121 - Prof. McLeod62 Mergesort – Cont. The sorting takes place in the merge method. But first, trace the execution path of the mergesort algorithm through a small array.

63 Summer 2007CISC121 - Prof. McLeod63 Trace Execution of Mergesort

64 Summer 2007CISC121 - Prof. McLeod64 Trace Execution of Mergesort start finish

65 Summer 2007CISC121 - Prof. McLeod65 Mergesort – “ aMergeSort ” Code Code for sorting arrays: public static void aMergeSort (int[] A) { aMergeSort(A, 0, A.length-1); } // end aMergeSort public static void aMergeSort (int[] A, int first, int last) { if (first < last) { int mid = (first + last) / 2; aMergeSort(A, first, mid); aMergeSort(A, mid + 1, last); aMerge(A, first, last); } // end if } // end aMergeSort recursive

66 Summer 2007CISC121 - Prof. McLeod66 Mergesort – “ aMerge ” Code private static void aMerge (int[] A, int first, int last) { int mid = (first + last) / 2; int i1 = 0, i2 = first, i3 = mid + 1; int[] temp = new int[last - first + 1]; while (i2 <= mid && i3 <= last) { if (A[i2] < A[i3]) { temp[i1] = A[i2]; i2++; } else { temp[i1] = A[i3]; i3++; } i1++; } // end while

67 Summer 2007CISC121 - Prof. McLeod67 Mergesort – “ aMerge ” Code - Cont. while (i2 <= mid) { temp[i1] = A[i2]; i2++; i1++; } // end while while (i3 <= last) { temp[i1] = A[i3]; i3++; i1++; } // end while i1 = 0; i2 = first; while (i2 <= last) { A[i2] = temp[i1]; i1++; i2++; } // end while } // end aMerge

68 Summer 2007CISC121 - Prof. McLeod68 Mergesort on a Linked List The following code shows how easy it is to sort a linked list using Mergesort. The advantage of Mergesort over Quicksort for linked lists is that you don’t have to worry about the potential problem of going inside the list for a pivot value.

69 Summer 2007CISC121 - Prof. McLeod69 public static IntSLList mergeSort (IntSLList L) { int n = L.size(); if (n < 2) return L; int i; IntSLList L1 = new IntSLList(); IntSLList L2 = new IntSLList(); for (i = n / 2; i < n; i++) L1.addToTail(L.removeFromHead()); while (!L.isEmpty()) L2.addToTail(L.removeFromHead()); L1 = mergeSort(L1); L2 = mergeSort(L2); L = merge(L1, L2); return L; } // end mergeSort

70 Summer 2007CISC121 - Prof. McLeod70 private static IntSLList merge (IntSLList L1, IntSLList L2) { IntSLList L = new IntSLList(); while (!L1.isEmpty() && !L2.isEmpty()) if (L1.lookAtHead() < L2.lookAtHead()) L.addToTail(L1.removeFromHead()); else L.addToTail(L2.removeFromHead()); while (!L1.isEmpty()) L.addToTail(L1.removeFromHead()); while (!L2.isEmpty()) L.addToTail(L2.removeFromHead()); return L; } // end merge

71 Summer 2007CISC121 - Prof. McLeod71 Why is Mergesort so Fast? Consider the “tree” constructed by the recursive calls: n n/2 n/4 n/8 And so on…

72 Summer 2007CISC121 - Prof. McLeod72 Why is Mergesort so Fast? - Cont. The dividing stops when the size of the subarray is one, or when n/2 i =1. As with binary search, this gives i=log(n), which is the “height” of the “tree”. At every line, n operations must take place to merge the elements, so the “width” of the tree is n. The total number of operations would be: 2 * n * log 2 (n). Simple sorts (bubble, insertion, selection) have n*n operations, on average.

73 Summer 2007CISC121 - Prof. McLeod73 Quicksort Quicksort was invented by C.A.R. Hoare in 1961. Uses “divide and conquer” just like Mergesort. Recursion allows the algorithm to be more easily coded. The algorithm starts by dividing the array into two subarrays based on a key value called the “pivot” or “bound” value. –All values less than the pivot go into the lower subarray, and all others go into the upper subarray. –Then each of these subarrays is subjected to the same process using a recursive call. –This continues until the subarrays are of size 1.

74 Summer 2007CISC121 - Prof. McLeod74 Quicksort – Cont. Pseudocode: Quicksort(array): –If array.length > 1: Choose pivot value. For all elements in array: –If element < pivot then it goes in array1, otherwise it goes in array2. Quicksort(array1). Quicksort(array2).

75 Summer 2007CISC121 - Prof. McLeod75 Quicksort – Cont. How to choose the pivot value? The idea is to get the subarrays to have about the same size. The simplest approach is to choose the first element of the array as the pivot. Better yet, if the array is already sorted to some degree, it will be safer to choose a value from the middle of the array, as is done in the code shown on the next few slides.

76 Summer 2007CISC121 - Prof. McLeod76 Code for “preparation” method: public static void quickSort(int[] A) { int max = 0; for (int i = 1; i < A.length; i++) if (A[i] > A[max]) max = i; swap(A, A.length-1, max); quickSort(A, 0, A.length-2); } // end quickSort(array) Quicksort – Cont.

77 Summer 2007CISC121 - Prof. McLeod77 This method locates the maximum element in A and moves it to the last position in the array. Prevents lower (in the next method) from running off the end of the array, in the remote chance that the pivot is chosen as the largest value in the array. (It also calls the typical “ swap ” method.) Quicksort – Cont.

78 Summer 2007CISC121 - Prof. McLeod78 public static void quickSort (int[] A, int first, int last) { int lower = first + 1; int upper = last; swap(A, first, (first+last)/2); int pivot = A[first]; while (lower <= upper) { while (A[lower] < pivot) lower++; while (A[upper] > pivot) upper--; if (lower < upper) swap(A, lower++, upper--); else lower++; } swap(A, upper, first); if (first < upper - 1) quickSort(A, first, upper-1); if (upper + 1 < last) quickSort(A, upper+1, last); } // end quickSort(subarrays)

79 Summer 2007CISC121 - Prof. McLeod79 Example - sorting array: 8 5 4 7 6 1 6 3 8 12 10 After “preparation”: 8 5 4 7 6 1 6 3 8 10 12 After selection of pivot value and initial assignment of lower and upper: 6 5 4 7 8 1 6 3 8 10 12 Upper and lower move in until a swap has to be made: 6 5 4 7 8 1 6 3 8 10 12 Make the swap and move to next swap to be made: 6 5 4 3 8 1 6 7 8 10 12 Max value pivot lowerupper pivot lowerupper pivot lowerupper

80 Summer 2007CISC121 - Prof. McLeod80 Make the swap, and then upper and lower pass each other: 6 5 4 3 6 1 8 7 8 10 12 Put pivot value back in, and divide into two subarrays: 1 5 4 3 6 6 8 7 8 10 12 (Note how old pivot and max value are already in correct positions and do not need to be included in next recursive call). pivot lowerupper

81 Summer 2007CISC121 - Prof. McLeod81 Two recursive calls with subarrays: 4 5 1 3 667 8 8 10 12 After upper and lower have moved through arrays and pivots have been replaced: 13 4 5 667 8 8 10 12 Last set of recursive calls with subarrays: 1 3 4 5 66 7 8 8 10 12 Array is sorted: 1 3 4 5 6 6 7 8 8 10 12 pivot

82 Summer 2007CISC121 - Prof. McLeod82 Quicksort – Cont. Note that new arrays are not created, only the bounds of the subarrays in the array A are changed. The recursive calls will only contain different bounding values. And the subarrays get smaller for each call. The anchor case is when the size of the subarray is one. No merge is required since the way the subarrays are create means that when they reach a size of one, they will be in order!

83 Summer 2007CISC121 - Prof. McLeod83 Quicksort - Cont. The worst case is when a near-median value is not chosen – the pivot value is always a maximum or a minimum value. Now the algorithm is just like a bubble sort. However, if the pivot values are always near the median value of the arrays, the algorithm is O(nlog(n)) – like Mergesort. In practice Quicksort is twice as fast as Mergesort.

84 Summer 2007CISC121 - Prof. McLeod84 Choice of Pivot Value Several techniques can be used: –Choose first value –Choose middle value –Calculate “pseudo-median”. This is how quicksort is implemented in Arrays.sort(). –Why not calculate the actual median? The technique you use will depend on how random the data set is.

85 Summer 2007CISC121 - Prof. McLeod85 Quicksort - Cont. So, the choice of the pivot value can be critical. Knowledge of the nature of the data to be sorted might suggest another algorithm to use in choosing the pivot value. Experiment has shown that Quicksort is faster than any other efficient sort. It has also been suggested that Quicksort can be made even faster by choosing to use a simple sort like insertion sort for subarray sizes less than about 30.

86 Summer 2007CISC121 - Prof. McLeod86 Comparison of Quicksort and Mergesort Mergesort is slower than Quicksort primarily because it does not do any preliminary shuffling of the data when it divides it, and the merge operation is required. It also requires more memory because of the creation of temporary arrays (or lists) to complete the merge operation.

87 Summer 2007CISC121 - Prof. McLeod87 Comparison of Quicksort and Mergesort However, merge sort is very useful when sorting data that does not have all be in memory at once (“in place”). For example, if the data must be saved on disk because it will not all fit in memory, then mergesort will make the minimum number of file reads and writes. If you have to sort lists, it is very easy to code mergesort for them, and you don’t have to worry about getting a pivot value out of a linked list.

88 Summer 2007CISC121 - Prof. McLeod88 Comparison of Sorts Sorting 50,000 random int ’s (average case) on a PIII. Selection sort is not influenced by the state of the data, where bubble and insertion are. SortTime (msec)# Comparisons# Swaps Insertion11000~623,000,00049,999 Selection18000~1,250,000,00050,000 Bubble40000~623,000,000 simple O(n 2 ) O(n)

89 Summer 2007CISC121 - Prof. McLeod89 Comparison of Sorts – Cont. SortTime (msec)# Comparisons# Swaps Shell50~1,300,000~1,784,000 Quicksort40360,000218,000 Merge (array)90718,100784,464 Merge (lists)1810718,100784,464 Radix (queues)960N/A1,000,000 Arrays.sort40?? Collections.sort (Vectors) 250?? Java Efficient O(n 1.25 ) O(nlog(n)) O(n)

90 Summer 2007CISC121 - Prof. McLeod90 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.


Download ppt "Summer 2007CISC121 - Prof. McLeod1 CISC121 – Lecture 10 Last time: –The Analysis of Complexity From now on, we will always want to know the complexity."

Similar presentations


Ads by Google