/*
* NthOrderStatistic.java
*
* Copyright (C) 2011 Leo Osvald <leo.osvald@gmail.com>
*
* This file is part of SGLJ.
*
* SGLJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SGLJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sglj.util;
import java.util.Arrays;
/**
* A set of methods for calculating nth smallest/largest element
* in linear time (proportional to the range being searched).<br>
*
* @author Leo Osvald
*
*/
public class NthOrderStatistic {
public static int nthElement(int[] a, int n, int fromIndex, int toIndex) {
rangeCheck(a, fromIndex, toIndex);
nthElement0(a, n + fromIndex, fromIndex, toIndex);
ArrayUtils.swap(a, fromIndex, n + fromIndex);
return a[fromIndex + n];
}
public static int nthElement(int[] a, int n) {
nthElement0(a, n, 0, a.length);
ArrayUtils.swap(a, 0, n);
return a[n];
}
private static int partition(int[] a, int fromIndex, int toIndex,
int pivotIndex) {
int pivotValue = a[pivotIndex];
ArrayUtils.swap(a, pivotIndex, toIndex - 1);
for (int i = fromIndex; i < toIndex; ++i) {
if (a[i] < pivotValue)
ArrayUtils.swap(a, i, fromIndex++);
}
ArrayUtils.swap(a, fromIndex, toIndex - 1);
return fromIndex;
}
/*
* Median of medians algorithm to find nth element in O(N)
*/
private static void nthElement0(int[] a, int n, int fromIndex, int toIndex) {
if (fromIndex < toIndex) {
int len = toIndex - fromIndex;
if (len < 0) {
for (int current = fromIndex; ++current < toIndex; ) {
int tmp = a[current];
int i = current;
for (int tmp1 = a[i - 1]; tmp < tmp1;
tmp1 = a[--i - 1] ) {
a[i] = tmp1;
if (fromIndex == i - 1) {
--i;
break;
}
}
a[i] = tmp;
}
ArrayUtils.swap(a, n, fromIndex);
} else {
int lo = fromIndex;
int groupCount = len / 5;
for (int group = 0; group < groupCount; ++group, lo += 5) {
int j = med5(a, lo, lo + 1, lo + 2, lo + 3, lo + 4);
ArrayUtils.swap(a, j, fromIndex + group);
}
nthElement0(a, fromIndex + (groupCount >>> 1),
fromIndex, fromIndex + groupCount);
int pivotIndex = partition(a, fromIndex, toIndex, fromIndex);
if (n < pivotIndex)
nthElement0(a, n, fromIndex, pivotIndex);
else if (n > pivotIndex) {
nthElement0(a, n, pivotIndex + 1, toIndex);
ArrayUtils.swap(a, pivotIndex + 1, fromIndex);
} else {
ArrayUtils.swap(a, pivotIndex, fromIndex);
}
}
return ;
}
assert false : "Bug detected"; // impossible
return ;
}
private static void rangeCheck(int[] a, int fromIndex, int toIndex) {
if (fromIndex < 0 || toIndex > a.length)
throw new IndexOutOfBoundsException();
if (fromIndex > toIndex)
throw new IllegalArgumentException();
}
public static int med5(int x[], int a, int b, int c, int d, int e) {
return x[b] < x[a] ? x[d] < x[c] ? x[b] < x[d] ? x[a] < x[e] ? x[a] < x[d] ? x[e] < x[d] ? e : d
: x[c] < x[a] ? c : a
: x[e] < x[d] ? x[a] < x[d] ? a : d
: x[c] < x[e] ? c : e
: x[c] < x[e] ? x[b] < x[c] ? x[a] < x[c] ? a : c
: x[e] < x[b] ? e : b
: x[b] < x[e] ? x[a] < x[e] ? a : e
: x[c] < x[b] ? c : b
: x[b] < x[c] ? x[a] < x[e] ? x[a] < x[c] ? x[e] < x[c] ? e : c
: x[d] < x[a] ? d : a
: x[e] < x[c] ? x[a] < x[c] ? a : c
: x[d] < x[e] ? d : e
: x[d] < x[e] ? x[b] < x[d] ? x[a] < x[d] ? a : d
: x[e] < x[b] ? e : b
: x[b] < x[e] ? x[a] < x[e] ? a : e
: x[d] < x[b] ? d : b
: x[d] < x[c] ? x[a] < x[d] ? x[b] < x[e] ? x[b] < x[d] ? x[e] < x[d] ? e : d
: x[c] < x[b] ? c : b
: x[e] < x[d] ? x[b] < x[d] ? b : d
: x[c] < x[e] ? c : e
: x[c] < x[e] ? x[a] < x[c] ? x[b] < x[c] ? b : c
: x[e] < x[a] ? e : a
: x[a] < x[e] ? x[b] < x[e] ? b : e
: x[c] < x[a] ? c : a
: x[a] < x[c] ? x[b] < x[e] ? x[b] < x[c] ? x[e] < x[c] ? e : c
: x[d] < x[b] ? d : b
: x[e] < x[c] ? x[b] < x[c] ? b : c
: x[d] < x[e] ? d : e
: x[d] < x[e] ? x[a] < x[d] ? x[b] < x[d] ? b : d
: x[e] < x[a] ? e : a
: x[a] < x[e] ? x[b] < x[e] ? b : e
: x[d] < x[a] ? d : a;
}
}