Mega Code Archive

 
Categories / Java / Collections Data Structure
 

Implements QuickSort three different ways

//package proj6; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /**  * Implements QuickSort three different ways:  * 1. Perform the basic, sequential QuickSort algorithm with two-pointer  *    partition.  * 2. Perform QuickSort using four threads, explicitely created and invoked.  * 3. Perform QuickSort on numThreads using Executor support from Java's  *    concurrency libraries.  *  * The program can be executed with the following command:  * Usage: java Project6 <part> [OPTIONS]  * <part> - Valid parts are 1, 2, or 3  *  * Available options:  * -s, --size    The size of the array to generate  * -r, --random  True indicates a random array, false indicates a seeded array  * -m, --max     Random numbers will generated between 0 (inclusive) and  *               maxValue  * -n, --num     Must be specified if you are desiring to run Part 3, otherwise  *               it is ignored.  *  * @author Alex Laird  * @author Ryan Morehart  */ public class Project6 {     /** The largest partition size that a task will execute for Part 3.*/     private final int LARGEST_SIZE = 1000;     /** Our processor ID if we are running in parallel. If the user is not running Part 2 or Part 3, this is left at -1.*/     private int id = -1;     /** In parallel algorithms, the number of threads that have completed their tasks.*/     private int finishedThreads = 0;     /** In parallel algorithms, the number of numbers that have been sorted.*/     private int finishedCount = 0;     /** The current number of tasks in use.*/     private int taskCount = 0;     /** Executor service for use in Part 3.*/     ExecutorService pool;     /** The number of threads to use if performing Part 2 or Part 3.*/     private int numThreads = -1;     /**      * Constructs the class object with the number of threads used, if running Part 2 or Part 3.      *      * @param numThreads Number of threads to use.      */     public Project6(int numThreads)     {         this.numThreads = numThreads;     }     /**      * Increments the variable stating the number of threads finished.      */     protected synchronized void threadFinished(int id)     {         ++finishedThreads;     }     /**      * Check if the number of slave threads reporting they have finished working      * is equal to the number of slave threads spawned.      *      * @return True if all spawned threads have finished, false otherwise.      */     private synchronized boolean waitingForThreads()     {         if (finishedThreads == numThreads)         {             return true;         }         return false;     }     /**      * Check if the number of sorted items equals the total number of elements      * in the array.      *      * @param size The number of elements total in the array.      * @return True if all elements have been sorted, false otherwise.      */     private synchronized boolean waitingForCount(int size)     {         if (getFinishedCount () != size)         {             return true;         }         return false;     }     /**      * Increment the finished counter by the given number n.      *      * @param n The amount to increment the finished counter by.      */     protected synchronized void addToFinishedCount (int n)     {         finishedCount += n;     }          /**      * Retrieve the number of finished threads.      *      * @return The number of finished threads.      */     private synchronized int getFinishedCount()     {         return finishedCount;     }     /**      * Increment the current number of tasks in use by one.      */     protected synchronized void incrementTaskCount()     {         ++taskCount;     }     /**      * Retrieve the current number of tasks running.      *      * @return The number of tasks running.      */     protected synchronized int getTaskCount()     {         return taskCount;     }     /**      * Swaps the two indeces in the array given.      *      * @param array The array to perform the swap on.      * @param first Swap this index with element at second.      * @param second Swap this index with element at first.      */     public void swap(int[] array,                      int first,                      int second)     {         int temp = array[first];         array[first] = array[second];         array[second] = temp;     }     /**      * Finds the most logical point to split the array in two parts and uses      * that point as the partition. Uses a while loop to evaluate the partition.      *      * @param array The array to be sorted.      * @param low The lowest index.      * @param high The highest index.      * @return The index of the partitioning point.      */     protected int partition(int[] array,                             int low,                             int high)     {         int pivot = array[high];         int left = low;         int right = high - 1;         while (left <= right)         {             // Increment the low pointer until you meet the pivot             while (left <= right &&                    array[left] <= pivot)             {                 ++left;             }             // Decrement the high pointer until you meet the pivot             while (left <= right &&                    array[right] >= pivot)             {                 --right;             }             // If the pointers have crossed, swap the items             if (left < right)             {                 // Swap value at left with value at right                 swap(array, left, right);             }         }         // Finally, swap value at left with value at high         swap (array, left, high);         return left;     }     /**      * Performs a standard recursive QuickSort algorithm on an array from      * indeces high to low.      *      * @param array The array to be sorted.      * @param low The lowest index.      * @param high The highest index.      */     protected void sort(int[] array,                         int low,                         int high)     {         if (low < high)         {             // Locate the most precise partition point             int mid = partition (array, low, high);             // Recursively sort the lower half             sort (array, low, mid - 1);             // Recursively sort the upper half             sort (array, mid + 1, high);         }     }     /**      * Continue passing partitions out to threads until the partition size is small      * enough for us to handle ourselves, backing out of recursion.      *      * @param low The lowest index.      * @param high The highest index.      * @param array The array to be sorted.      */     protected void part3(int low,                          int high,                          int[] array)     {         if (high - low + 1 < LARGEST_SIZE)         {             // The partition is small enough, so do the work ourselves             sort (array, low, high);             addToFinishedCount (high - low + 1);         }         else         {             // Identify partition point             int mid = partition (array, low, high);             addToFinishedCount (1);             // Launch slave task             pool.execute (new QuickSortTask (this, getTaskCount (), mid + 1, high, array));             incrementTaskCount ();             // Recurse to assign the lower half to its thread             part3(low, mid - 1, array);         }     }     /**      * Generate the array, sort it using the correct method for the given part,      * and benchmark.      *      * @param part The part of the problem to perform.  Value use be 1, 2, or 3      * @param size The size of the array to sort.      * @param random True if numbers should be generated randomly, false if they should follow a pattern      * @param maxValue The maximum value a randomly generated number can be      */     private void go(int part,                     int size,                     boolean random,                     int maxValue)     {         // Generate array, output if length is reasonable, and declare time variables before         // starting the time count, so as to not effect performance         int[] array = Utility.buildArray (size, random, maxValue);         // Copy the array to an unsorted array location for verification after the fact         int[] unsortedArray = Arrays.copyOf (array, array.length);         System.out.println ("::Part " + part + "::");         System.out.println ("Array size: " + Utility.NUM_FORMAT.format (size));         if (array.length <= 25)         {             System.out.print ("Initial array: ");             Utility.printArray (array);         }                  long startTime = -1;         long endTime = -1;         // Mark beginning time for calculation later         startTime = System.currentTimeMillis();         // Perform the basic, sequential QuickSort algorithm with two-pointer partition         if (part == 1)         {             sort (array, 0, array.length - 1);         }         // Perform QuickSort using four threads, explicitely created and invoked         else if (part == 2)         {             // Set this processor's ID             id = 0;             // Set the number of slave threads             numThreads = 3;                          int low = 0;             int high = array.length - 1;             // Identify partition points for master and slaves             int mid = partition (array, low, high);             int first = partition (array, low, mid - 1);             int second = partition (array, mid + 1, high);             // Instantiate slave threads and pass them their partition portions             QuickSortThread thread2 = new QuickSortThread (this, 1, first + 1, mid - 1, array);             QuickSortThread thread3 = new QuickSortThread (this, 2, mid + 1, second - 1, array);             QuickSortThread thread4 = new QuickSortThread (this, 3, second + 1, high, array);             // Launch slave threads             thread2.start ();             thread3.start ();             thread4.start ();             // Current thread is the first thread, so let it do the lowest part             sort (array, low, first - 1);             // Wait for all threads to finish             while (!waitingForThreads ())             {                 continue;             }         }         // Perform QuickSort on numThreads using Executor support from Java's concurrency libraries         else if (part == 3)         {             // Set this processor's ID and increment task count             id = 0;             incrementTaskCount ();             // Launch slave task             pool = Executors.newFixedThreadPool (numThreads);             pool.execute (new QuickSortTask (this, getTaskCount (), 0, array.length - 1, array));             incrementTaskCount ();             while (waitingForCount (array.length))             {                 continue;             }             pool.shutdown();         }         // Mark end time and calculate total runtime         endTime = System.currentTimeMillis();         long totalTime = endTime - startTime;         // Output benchmark as well as results, if length is reasonable         if (part == 2)         {             System.out.println ("Threads used: 4");         }         else if(part == 3)         {             System.out.println ("Tasks used: " + numThreads);         }         System.out.println ("Elapsed time: " + Utility.NUM_FORMAT.format (totalTime) + "ms");         if (array.length <= 25)         {             System.out.print ("Sorted array: ");             Utility.printArray (array);         }         if (Utility.verifyArray (array, unsortedArray))         {             System.out.println ("Verified: array sorted properly");         }         else         {             System.out.println ("Unverified: array did not sort properly");         }         System.out.println ();     }     /**      * The main method executes first in the program, parsing command-line arguments      * and then immediately leaving static-land for the actual algorithms and      * computations.      *      * @param args The command-line arguments.  "Usage: java Project6 <part> [OPTIONS]      * <part> - Valid parts are 1, 2, or 3      *      * Available options:      * -s, --size    The size of the array to generate      * -r, --random  True indicates a random array, false indicates a seeded array      * -m, --max     Random numbers will generated between 0 (inclusive) and maxValue      * -n, --num     Must be specified if you are desiring to run Part 3, otherwise it is ignored.      */     public static void main(String[] args)     {         // Output string for common errors         final String USAGE_TEXT = "Usage: java Project6 <part> [OPTIONS]\n"         + "<part> - Valid parts are 1, 2, or 3\n\n"         + "Available options:\n"         + "-s, --size      The size of the array to generate\n"         + "-r, --random    True indicates a random array, false indicates a seeded array\n"         + "-m, --max       Random numbers will generated between 0 (inclusive) and maxValue\n"         + "-n, --num       Must be specified if you are desiring to run Part 3, otherwise it is ignored.";         // The part of the problem to perform.  Value use be 1, 2, or 3         int part = -1;         // The size of the array to sort.*/         int size = 10;         // True if numbers should be generated randomly, false if they should follow a pattern         boolean random = false;         // The maximum value a randomly generated number can be         int maxValue = 10;         // The number of threads to use if performing Part 3         int numThreads = -1;                  // Parse through arguments and set their respective variables         if (args.length > 0)         {             part = Integer.parseInt (args[0]);             try             {                 for (int i = 1; i < args.length; i += 2)                 {                     if (args[i].equals ("-s"))                     {                         size = Integer.parseInt (args[i + 1]);                     }                     else if(args[i].equals ("-r"))                     {                         if (args[i + 1].equals ("true"))                         {                             random = true;                         }                         else if (!args[i + 1].equals ("false"))                         {                             int intRand = Integer.parseInt (args[i + 1]);                             if (intRand == 1)                             {                                 random = true;                             }                             else if (intRand != 0)                             {                                 // Since the given random value was not valid at this point, just break the given part so the program termiantes                                 part = -1;                             }                         }                     }                     else if(args[i].equals("-m"))                     {                         maxValue = Integer.parseInt(args[i + 1]);                     }                     else if(args[i].equals("-n"))                     {                         numThreads = Integer.parseInt(args[i + 1]);                     }                 }             }             catch (NumberFormatException ex)             {                 System.out.println (USAGE_TEXT);                 System.exit (2);             }             // Make sure the given part number was valid             if (part > 3 || part < 1)             {                 System.out.println (USAGE_TEXT);                 System.exit (3);             }             // If we are doing Part 3 and the number of desired threads was not given, we cannot continue             if (part == 3 && numThreads == -1)             {                 System.out.println (USAGE_TEXT);                 System.exit (4);             }         }         else         {             System.out.println (USAGE_TEXT);             System.exit (1);         }         // Get us out of static-land!         Project6 proj6 = new Project6 (numThreads);         proj6.go (part, size, random, maxValue);     } } class QuickSortTask implements Runnable {     /** The back-reference to the class containing the sorting routine.*/     Project6 proj6;     /** The ID of this task.*/     int id;     /** The lowest index this task goes in the array.*/     int low;     /** The highest index this task goes in the array.*/     int high;     /** The array to be sorted.*/     int[] array;     /**      * Construct a new task, giving it a class reference containing the sorting      * routine as well as the array to be sorted.      *      * @param proj6 Pass the reference to the class containing necessary sorting routine.      * @param id The ID of this task.      * @param low The lowest index this task goes in the array.      * @param high The highest index this task goes in the array.      * @param array The array to be sorted.      */     public QuickSortTask (Project6 proj6,                           int id,                           int low,                           int high,                           int[] array)     {         this.proj6 = proj6;         this.id = id;         this.low = low;         this.high = high;         this.array = array;     }     /**      * Instantiates the task and performs the sort on the given array for the      * specified portions.      */     @Override     public void run()     {         proj6.part3 (low, high, array);     } } /**  * This class contains common, simple helper functions that are globally  * available to any class that needs them.  *  * @author Alex Laird  * @author Ryan Morehart  */  class Utility {     /** The number formatter for pretty output.*/     public static final NumberFormat NUM_FORMAT = new DecimalFormat ("###,###");          /**      * Builds an array of given size with random values (if random is true) between      * 0 (inclusive) and MAX_VALUE.  If random is set to false, values are seeded to      * the indeces of the array.      *       * @param size The size to build the array.      * @param random True if values should be random, false if values should be seeded.      * @param MAX_VALUE The maximum value of a randomly generated number.      * @return The newly built array.      */     public static int[] buildArray (int size,                                     boolean random,                                     final int MAX_VALUE)     {         int[] array = new int[size];         Random randomizer = new Random ();         // Loop through and set all array values         for (int i = 0; i < size; ++i)         {             // If random, set value between 0 (inclusive) and MAX_VALUE             if (random)             {                 array[i] = randomizer.nextInt(MAX_VALUE);             }             // Not random, so set value to the index of the array             else             {                 array[i] = i;             }         }         return array;     }     /**      * Verify that the two arrays are equal by sorting the unsorted array, using      * verified Java sorting libraries, and comparing the two arrays.      *      * @param sortedArray The sorted array.      * @param unsortedArray The unsorted array to be sorted and compared to the sorted array.      * @return True if the arrays are equal after sorting using Java sorting libraries, false if there was an inconsistency.      */     public static boolean verifyArray(int[] sortedArray,                                       int[] unsortedArray)     {         Arrays.sort (unsortedArray);         for (int i = 0; i < sortedArray.length ;++i)         {             if (sortedArray[i] != unsortedArray[i])             {                 return false;             }         }         return true;     }     /**      * Print the given array.      *      * @param array The array to be printed.      */     public static void printArray(int[] array)     {         for (int i = 0; i < array.length; ++i)         {             System.out.print (array[i]);             if (i < array.length - 1)             {                 System.out.print (", ");             }         }         System.out.println ();     } }  /**   * A thread that will sort a set array upon execution.   *   * @author Alex Laird   * @author Ryan Morehart   */   class QuickSortThread extends Thread  {      /** The back-reference to the class containing the sorting routine.*/      Project6 proj6;      /** The ID of this thread.*/      int id;      /** The lowest index this thread goes in the array.*/      int low;      /** The highest index this thread goes in the array.*/      int high;      /** The array to be sorted.*/      int[] array;      /**       * Construct a new thread, giving it a class reference containing the sorting       * routine as well as the array to be sorted.       *        * @param proj6 Pass the reference to the class containing necessary sorting routine.       * @param id The ID of this thread.       * @param low The lowest index this thread goes in the array.       * @param high The highest index this thread goes in the array.       * @param array The array to be sorted.       */      public QuickSortThread (Project6 proj6,                              int id,                              int low,                              int high,                              int[] array)      {          this.proj6 = proj6;          this.id = id;          this.low = low;          this.high = high;          this.array = array;      }      /**       * Instantiates the thread and performs the sort on the given array for the       * specified portions.       */      @Override      public void run()      {          proj6.sort (array, low, high);          proj6.threadFinished (id);      }  }