Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Optimized QuickSort Implementation in Java - Prof. Timothy Rolfe, Assignments of Algorithms and Programming

An implementation of the optimized quicksort algorithm in java, including the use of a random pivot, median-of-three pivot, and insertion sort for segments with fewer than 10 entries. The benchmark program includes two optimizations and accepts initial and final array sizes, increment for array size, and the number of random arrays to be averaged.

Typology: Assignments

Pre 2010

Uploaded on 08/16/2009

koofers-user-tqk
koofers-user-tqk 🇺🇸

10 documents

1 / 8

Toggle sidebar

Related documents


Partial preview of the text

Download Optimized QuickSort Implementation in Java - Prof. Timothy Rolfe and more Assignments Algorithms and Programming in PDF only on Docsity! Benchmarking of Pivot Selection Strategies Timothy Rolfe The detailed behavior of QuickSort depends on the strategy used for the selection of the pivot value within the Partition algorithm. The fastest method is simply to accept the value at the beginning of the region being sorted. As Tony Hoare, the originator of QuickSort, noted in his original discussion of the algorithm1, this is known to have disastrous results if the data being sorted are already ordered, either in a forward direction or reversed: QuickSort becomes an O(n2) algorithm rather than the average case as an O(n log n) algorithm. One can avoid this worst-case behavior by choosing a random location within the region being sorted. This has the overhead of computing the random location — the method invocation of calling the random number generator, the integer arithmetic of computing the complete subscript, and the three assignment statements to swap the front element with that random element. This is actually in the original publication of the algorithm.2 Finally, Sedgewick suggests a median-of-three algorithm.3 Assume that the region being sorted is defined by subscripts “lo” and “hi”. Compute the midpoint subscript as “(lo+hi)/2”. Perform the necessary comparisons and swaps to guarantee that the median of the three values is at x[lo]. Below is my implementation in Java, which sorts [lo], [mid], and [hi]: mid = (lo + hi) / 2; // We wish to move the median -- b in "abc" -- to position lo if (x[lo].compareTo(x[hi]) < 0) // abc acb bac if (x[lo].compareTo(x[mid]) < 0) // abc acb if (x[mid].compareTo(x[hi]) < 0) // abc swap(lo, mid, x); else // acb --- place sentinels { swap(lo, hi, x); swap (mid, hi, x); } else ; // bac --- null statement else // bca cab cba if (x[lo].compareTo(x[mid]) > 0) // cab cba if (x[mid].compareTo(x[hi]) > 0) // cba --- place sentinels { swap(lo, mid, x); swap (mid, hi, x); } else // cab swap(lo, hi, x); else swap(mid, hi, x); // bca: move c into place On average, there are 8/3 invocations of compareTo: four permutations require three, while two require two. Seven swaps are performed across the six possible permutations. Thus we probably have a somewhat larger overhead than the random pivot. Given the complexity of the above, it might be preferable to use a pure three-comparison method with potentially three swaps, as given in Koffman's implementation.4 1 Charles Anthony Richard Hoare, Computer Journal, Vol 2 (1962), pp. 10-15. 2 Charles Anthony Richard Hoare, CACM, Vol 4, No. 7 (July 1961), pp. 321-22. 3 Robert Sedgewick, Algorithms in C; Parts 1-4 (3rd edition; Addison, Wesley, 1998), p. 319-24. Printed 2020-11月-30 at 19:14 Page 1 Note: Two-sided Pages Benchmarking of . . . Page 2 // "Three-sort" the entries in [lo], [mid], and [hi] if ( x[mid].compareTo(x[lo]) < 0 ) swap ( lo, mid, x ); // assert: x[lo] <= x[mid] if ( x[hi].compareTo(x[mid]) < 0 ) swap ( mid, hi, x ); // assert: x[hi] is the largest value of the three if ( x[mid].compareTo(x[lo]) < 0 ) swap ( lo, mid, x ); // assert: x[lo] <= x[mid] <= x[hi] // Begin partition logic: move partitioning element to [lo] swap ( lo, mid, x ); The benchmark program uses a global variable “PIVOT” which contains 0, 1, or 2. Within the partition method, the pivot value is assumed to be in x[lo]. If PIVOT==1, a random location within the region [lo..hi] is swapped with x[lo]. If PIVOT==2, the above logic sets the median value into that location. The rest of the partition method uses an implementation I have taken from Thomas Naps 5 (and was included in Hoare's original discussion1). The QuickSort implementation used includes two optimizations: (1) if the array segment to be sorted contains fewer than 10 entries, the insertion sort is used on it; (2) since the sorting of the right-hand segment is tail recursive, that recursive call is replaced by a loop depending on the update of parameters — the initial “if” becomes “while (hi > lo)”, and the recursive call is replaced by the updated “hi = mid + 1”. The main accepts from the user (or the command line) the initial array size, the final array size, the increment for the array size, and the number of random arrays to be averaged — given the current speed of computers, we cannot measure the time required to sort a single array except for sizes that would probably exceed physical memory available. The data show that selection of a random pivot does indeed slow down the sorting, while the median-of-three speeds it up. The program was run on wyoming.ewu.edu, a computer with a single 2 GHz Intel® Pentium®–4 processor, within the Red Hat Linux and Java 1.4.2 environments. 4 Elliot B. Koffman and Paul A. T. Wolfgang, Objects, Abstraction, Data Structures and Design using Java Version 5.0 (John Wiley & Sons, 2005), pp. 551-53. 5 Thomas L. Naps, Introduction to Data Structures and Algorithm Analysis (2nd edition; West Publishing Company, 1992), pp. 261-62. Printed 2020-11月-30 at 19:14 Benchmarking of . . . Page 5 CkPivot.java /** * Exercise "choosePivot" options * * @author: Timothy Rolfe */ import java.io.*; import java.util.StringTokenizer; class CkPivot {//Random generator is seeded in main, but used in shuffle. static java.util.Random generator = new java.util.Random(); static final boolean DISPLAY = false; // Display results static final boolean CHECK = true ; // Check validity of sorting static final int CUTOFF = 10; // Length to shift to inSort static int PIVOT; // front/random/median-of-3 static BufferedReader console = new BufferedReader (new InputStreamReader(System.in)); static StringTokenizer tk = new StringTokenizer(""); /** * Return one line from System.in */ static String readLine() throws Exception { return console.readLine(); } static int readInt() { try { while (tk.countTokens() == 0 ) tk = new StringTokenizer(readLine()); return Integer.parseInt(tk.nextToken()); } catch (Exception e) { System.out.println(e); System.exit(-1); } return Integer.MIN_VALUE; } /** * Public access to optimized quick sort */ public static void qSortO (Comparable [] x, int n) { qSortO(0, n-1, x); } /** * Optimized quick sort --- insertion sort for segments < CUTOFF */ static void qSortO(int lo, int hi, Comparable [] x) {/* Basic QuickSort algorithm: 1) Check for exit condition: if hi does not come after lo, there is nothing left to sort. 2) Let the "partition" function position one element to its exact position: everything to its left belongs on the left, everything to its right belongs on its right. 3) QuickSort can then call itself to sort everything to the left as a sub-array. 4) Rather than recursively calling itself for the right sub-array, QuickSort can just update lo and stay within the current call, thus removing the tail recursion. Note: Almost _all_ of the work for qSort is embedded in the partition routine. */ int mid; while (lo < hi) // while allows for removing tail recursion { if ( hi-lo < CUTOFF ) // Small case: Insert Sort { inSort(lo, hi, x); break; } mid = partition(lo, hi, x); qSortO(lo, mid - 1, x); // Recursive part for left lo = mid + 1; // "Tail" recursion on right } // end while } /** * Swap two elements in an array --- used by partition */ static void swap (int lt, int rt, Object [] x) { Object tmp = x[lt]; x[lt] = x[rt]; x[rt] = tmp; } /** * Partition method used by quick sort methods. */ static int partition (int lo, int hi, Comparable [] x) {/*Rearrange the x[] array so that a single element is properly positioned: all elements to the left of the "partitioning element" (or pivot) belong on the left; all to the right belong on the right. The position of this partitioning element is then the value of the partition function. This version of partition based on Thomas Naps, "Introduction to Data Structures and Algorithm Analysis"; the Median of Three improvement is taken from Robert Sedgewick, "Algorithms." */ Printed 2020-11月-30 at 19:14 Benchmarking of . . . Page 6 int mid; Comparable hold; if (lo >= hi) return hi; if ( PIVOT == 1 ) // Random point chosen for pivot value { // Uses Math.random to avoid changing the sequence in generator hold = x[lo]; mid = (int) (Math.random() * ((hi+1) - lo)) + lo; x[lo] = x[mid]; x[mid] = hold; } else if ( PIVOT == 2 ) // Median-of-3 value chosen for pivot value { // "Median of Three" --- middle element becomes the actual element. // Insure that ends up in x[lo]. mid = (lo + hi) / 2; // We wish to move the median -- b in "abc" -- to position lo // We wish to move the median -- b in "abc" -- to position lo if (x[lo].compareTo(x[hi]) < 0) // abc acb bac if (x[lo].compareTo(x[mid]) < 0) // abc acb if (x[mid].compareTo(x[hi]) < 0) // abc swap(lo, mid, x); else // acb --- place sentinels { swap(lo, hi, x); swap (mid, hi, x); } else ; // bac --- null statement else // bca cab cba if (x[lo].compareTo(x[mid]) > 0) // cab cba if (x[mid].compareTo(x[hi]) > 0) // cba --- place sentinels { swap(lo, mid, x); swap (mid, hi, x); } else // cab swap(lo, hi, x); else swap(mid, hi, x); // bca: move c into place } // Open up a "hole" at x[lo], with median as pivot value hold = x[lo]; // The loop has two exits: the two places where the lo and the hi // indexes have come together. In lieu of extra flags or logical // comparisons, this code uses an explicit "break" at those 2 places. while (true) // Note: "break" out when (lo == hi) {//Search for the value from hi downward to plug the hole at x[lo] while (hold.compareTo(x[hi]) < 0 && lo < hi) hi = hi - 1; // If we've come together, we're done if (lo == hi) break; // Otherwise plug the hole at lo with the value at hi x[lo] = x[hi]; // The hole is now at hi and lo is guaranteed good w.r.t. Pivot lo = lo + 1; // Search for the value from lo upward to plug the hole at x[hi] while (x[lo].compareTo(hold) < 0 && lo < hi) lo = lo + 1; // If we've come together, we're done if (lo == hi) break; // Otherwise plug the hole at hi with the value at lo x[hi] = x[lo]; // The hole is now at lo and hi is guaranteed good w.r.t. Pivot hi = hi - 1; } // end "infinite" while loop --- (lo == hi) became true // Plug the remaining hole (which is guaranteed to be in hi = lo) // with the pivot value, making this the partitioning element. x[hi] = hold; return hi; } /** * Insertion sort method, used by qSortO */ public static void inSort ( int lo, int hi, Comparable[] x ) { int lim, // start of unsorted region hole; // insertion point in sorted region for ( lim = lo+1 ; lim <= hi ; lim++ ) { Comparable save = x[lim]; for ( hole = lim ; hole > lo && save.compareTo(x[hole-1]) < 0 ; hole-- ) { x[hole] = x[hole-1]; } x[hole] = save; } // end for (lim... } // end inSort() /** * Driving main to exercise the quick sort algorithms */ public static void main ( String [] args ) { Integer [] test; // Array of int objects int size, maxSize, sizeStep; // control outer loop int nPasses, pass; // inner loop double start, // initial times in msec. elapsed[] = new double[3]; // three elapsed times System.out.print ("Initial array length: "); if ( args.length < 1 ) size = readInt(); else { size = Integer.parseInt(args[0]); System.out.println ("" + size); } // end if/else regarding args[0] Printed 2020-11月-30 at 19:14 Benchmarking of . . . Page 7 System.out.print ("Maximum array length: "); if ( args.length < 2 ) maxSize = readInt(); else { maxSize = Integer.parseInt(args[1]); System.out.println ("" + maxSize); } // end if/else regarding args[1] System.out.print ("Increment for length: "); if ( args.length < 3 ) sizeStep = readInt(); else { sizeStep = Integer.parseInt(args[2]); System.out.println ("" + sizeStep); } // end if/else regarding args[2] if ( DISPLAY ) nPasses = 1; else { System.out.print ("Number of passes: "); if ( args.length < 4 ) nPasses = readInt(); else { nPasses = Integer.parseInt(args[3]); System.out.println ("" + nPasses); } // end if/else regarding args[3] } // end if/else test = new Integer [maxSize]; fillArray ( test ); System.out.println ("n,front,random,median-of-three"); while ( size <= maxSize ) { long seed = generator.nextLong(); // Same sequence in all 3 // Partition uses first item as pivot start = System.currentTimeMillis(); generator.setSeed(seed); PIVOT = 0; for ( pass = 0; pass < nPasses; pass++ ) { shuffleArray ( test, size ); if (DISPLAY) showArray ( test, size ); qSortO ( test, size ); if ( CHECK && !sorted ( test, size ) ) { System.out.println("Sort failed. Abort in pass " + (pass+1)); break; } } // end for elapsed[0] = (System.currentTimeMillis() - start)/1000.0; // Partition uses a random pivot start = System.currentTimeMillis(); generator.setSeed(seed); PIVOT = 1; for ( pass = 0; pass < nPasses; pass++ ) { shuffleArray ( test, size ); if (DISPLAY) showArray ( test, size ); qSortO ( test, size ); if ( CHECK && !sorted ( test, size ) ) { System.out.println("Sort failed. Abort in pass " + (pass+1)); break; } } // end for elapsed[1] = (System.currentTimeMillis() - start)/1000.0; // Partition uses median-of-three for pivot start = System.currentTimeMillis(); generator.setSeed(seed); PIVOT = 2; for ( pass = 0; pass < nPasses; pass++ ) { shuffleArray ( test, size ); if (DISPLAY) showArray ( test, size ); qSortO ( test, size ); if ( CHECK && !sorted ( test, size ) ) { System.out.println("Sort failed. Abort in pass " + (pass+1)); break; } } // end for elapsed[2] = (System.currentTimeMillis() - start)/1000.0; if (DISPLAY) { System.out.println (""); showArray ( test, size ); } System.out.print (size+""); for ( int k = 0; k < elapsed.length; k++ ) System.out.print ( "," + elapsed[k] ); System.out.println(""); size += sizeStep; // Next size } // end while ( size <= maxSize ) } // end main() /** * Fill the array with increasing sequence starting at 1 */ static void fillArray ( Integer [] x ) { for ( int k = 0; k < x.length; k++ ) x[k] = new Integer(k+1); } // end fillArray() Printed 2020-11月-30 at 19:14
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved