package vroom.common.utilities.math;
import java.util.Arrays;
import java.util.Random;
import vroom.common.utilities.Utilities;
/**
* The class <code>QuickSelect</code> contains an implementation of <em>quicksort</em> that can be used to find the k
* lowest or greatest elements of an array.
* <p>
* Freely inspired from <a href=
* "http://www.java-tips.org/java-se-tips/java.lang/quickselect-implementation-with-median-of-three-partitioning-and-cutoff.html"
* >http://www.java-tips.org/java-se-tips/java.lang/quickselect-implementation-with-median-of-three-partitioning-and-
* cutoff.html</a>
* <p>
* <cite>Quicksort with median-of-three partitioning functions nearly the same as normal quicksort with the only
* difference being how the pivot item is selected. In normal quicksort the first element is automatically the pivot
* item. This causes normal quicksort to function very inefficiently when presented with an already sorted list. The
* divison will always end up producing one sub-array with no elements and one with all the elements (minus of course
* the pivot item). In quicksort with median-of-three partitioning the pivot item is selected as the median between the
* first element, the last element, and the middle element (decided using integer division of n/2). In the cases of
* already sorted lists this should take the middle element as the pivot thereby reducing the inefficency found in
* normal quicksort.</cite>
* </p>
* <p>
* Creation date: Feb 29, 2012 - 2:54:21 PM
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a
* href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0
*/
public class QuickSelect {
/**
* Returns the <code>k</code> lowest values of an array
* <p>
* Complexity is in <code>O(array.length)</code>, this implementation will accept {@code null} values if there is at
* least {@code k} non {@code null} values
* </p>
*
* @param values
* an array of value
* @param k
* the number of elements to extract
* @param preserveArray
* {@code true} if the original array should not be modified, {@code false} if its elements can be
* reordered
* @param sort
* {@code true} if the result should be sorted (according to their natural order)
* @return the he <code>k</code> lowest values of <code>values</code>
*/
public static <T extends Comparable<? super T>> T[] min(T[] values, int k, boolean preserveArray, boolean sort) {
return quickSelect(values, k, preserveArray, sort, true);
}
/**
* Returns the <code>k</code> greatest values of an array
* <p>
* Complexity is in <code>O(array.length)</code>, this implementation will accept {@code null} values if there is at
* least {@code k} non {@code null} values
* </p>
*
* @param values
* an array of value
* @param k
* the number of elements to extract
* @param preserveArray
* {@code true} if the original array should not be modified, {@code false} if its elements can be
* reordered
* @param sort
* {@code true} if the result should be sorted (according to their natural order)
* @return the he <code>k</code> greatest values of <code>values</code>
*/
public static <T extends Comparable<? super T>> T[] max(T[] values, int k, boolean preserveArray, boolean sort) {
return quickSelect(values, k, preserveArray, sort, false);
}
/**
* Returns the <code>k</code> lowest or greatest values of an array
* <p>
* Complexity is in <code>O(array.length)</code>, this implementation will accept {@code null} values if there is at
* least {@code k} non {@code null} values. In the contrary, the returned array will only contain non {@code null}
* values and will therefore be of smaller length.
* </p>
*
* @param values
* an array of value
* @param k
* the number of elements to extract
* @param preserveArray
* {@code true} if the original array should not be modified, {@code false} if its elements can be
* reordered
* @param sort
* {@code true} if the result should be sorted (according to their natural order)
* @param min
* {@code true} for the <code>k</code> lowest values, {@code false} for the <code>k</code> greatest
* values
* @return the he <code>k</code> lowest or greatest values of <code>values</code>
*/
public static <T extends Comparable<? super T>> T[] quickSelect(T[] values, int k, boolean preserveArray,
boolean sort, boolean min) {
T[] clone = preserveArray ? Arrays.copyOf(values, values.length) : values;
quickSelect(clone, k, min);
// The number of non-null elements
int nonNullLength = Math.min(k, clone.length);
// Reduce the length of the result array so that it does not contains any null values
while (clone[nonNullLength] == null && nonNullLength > 0)
nonNullLength--;
T[] sel = Arrays.copyOf(clone, nonNullLength);
if (sort)
Arrays.sort(sel);
return sel;
}
/**
* Quick selection algorithm. Places the kth lowest/greatest item in a[k-1].
* <p>
* This implementation will accept {@code null} values if there is at least {@code k} non {@code null} values
* </p>
*
* @param a
* an array of Comparable items.
* @param k
* the desired rank (1 is minimum) in the entire array
* @param min
* {@code true} for the <code>k</code> lowest values, {@code false} for the <code>k</code> greatest
* values
* @return the kth lowest/greatest item
*/
public static <T extends Comparable<? super T>> T quickSelect(T[] a, int k, boolean min) {
return quickSelect(a, 0, a.length - 1, k, min);
}
/**
* Internal selection method that makes recursive calls. Uses median-of-three partitioning and a cutoff of 10.
* Places the kth smallest item in a[k-1].
*
* @param a
* an array of Comparable items.
* @param low
* the left-most index of the subarray.
* @param high
* the right-most index of the subarray.
* @param k
* the desired rank (1 is minimum) in the entire array.
* @param min
* {@code true} for the <code>k</code> lowest values, {@code false} for the <code>k</code> greatest
* values
* @return the kth lowest/greatest item
*/
private static <T extends Comparable<? super T>> T quickSelect(T[] a, int low, int high, int k, boolean min) {
if (k > a.length)
k = a.length;
if (low + CUTOFF > high)
insertionSort(a, low, high, min);
else {
// Sort low, middle, high
int middle = (low + high) / 2;
if (compare(a[middle], a[low], min) < 0)
swapReferences(a, low, middle);
if (compare(a[high], a[low], min) < 0)
swapReferences(a, low, high);
if (compare(a[high], a[middle], min) < 0)
swapReferences(a, middle, high);
// Place pivot at position high - 1
swapReferences(a, middle, high - 1);
T pivot = a[high - 1];
// Begin partitioning
int i, j;
for (i = low, j = high - 1;;) {
while (compare(a[++i], pivot, min) < 0)
;
while (compare(pivot, a[--j], min) < 0)
;
if (i >= j)
break;
swapReferences(a, i, j);
}
// Restore pivot
swapReferences(a, i, high - 1);
// Recurse; only this part changes
if (k <= i)
quickSelect(a, low, i - 1, k, min);
else if (k > i + 1)
quickSelect(a, i + 1, high, k, min);
}
return a[k - 1];
}
/**
* Internal insertion sort routine for subarrays that is used by quicksort.
*
* @param a
* an array of Comparable items.
* @param low
* the left-most index of the subarray.
* @param n
* the number of items to sort.
*/
private static <T extends Comparable<? super T>> void insertionSort(T[] a, int low, int high, boolean min) {
for (int p = low + 1; p <= high; p++) {
T tmp = a[p];
int j;
for (j = p; j > low && compare(tmp, a[j - 1], min) < 0; j--)
a[j] = a[j - 1];
a[j] = tmp;
}
}
/**
* Adjusted comparison for the max version
*
* @param o1
* @param o2
* @param min
* {@code true} for min version, {@code false} for max version
* @return {@code +/- o1.compareTo(o2)}
*/
private static <T extends Comparable<? super T>> int compare(T o1, T o2, boolean min) {
if (o1 == null)
return 1;
else if (o2 == null)
return -1;
else
return min ? o1.compareTo(o2) : -o1.compareTo(o2);
}
private static final int CUTOFF = 10;
/**
* Method to swap to elements in an array.
*
* @param a
* an array of objects.
* @param index1
* the index of the first object.
* @param index2
* the index of the second object.
*/
public static final void swapReferences(Object[] a, int index1, int index2) {
Object tmp = a[index1];
a[index1] = a[index2];
a[index2] = tmp;
}
public static void main(String[] args) {
Random r = new Random();
Integer[] array = new Integer[500];
for (int i = 0; i < array.length; i++)
array[i] = r.nextInt(1000);
array[0] = null;
int k = 10;
// ----------------------
quickSelect(array, 10, true);
for (int i = 0; i < k; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
System.out.println(Utilities.toShortString(quickSelect(array, 10, true, true, true)));
System.out.println("Incoherencies:");
for (int i = 0; i < k; i++) {
for (int j = k; j < array.length; j++)
if (array[i] != null && array[j] != null && array[j] < array[i])
System.out.println(array[j]);
}
// ----------------------
System.out.println();
quickSelect(array, 10, false);
for (int i = 0; i < k; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
System.out.println(Utilities.toShortString(quickSelect(array, 10, true, true, false)));
System.out.println("Incoherencies:");
for (int i = 0; i < k; i++) {
for (int j = k; j < array.length; j++)
if (array[i] != null && array[j] != null && array[j] > array[i])
System.out.println(array[j]);
}
}
}