KthSelector.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.util;

  22. import java.io.Serializable;
  23. import java.util.Arrays;

  24. import org.hipparchus.exception.NullArgumentException;


  25. /**
  26.  * A Simple K<sup>th</sup> selector implementation to pick up the
  27.  * K<sup>th</sup> ordered element from a work array containing the
  28.  * input numbers.
  29.  */
  30. public class KthSelector implements Serializable {

  31.     /** Serializable UID. */
  32.     private static final long serialVersionUID = 20140713L;

  33.     /** Minimum selection size for insertion sort rather than selection. */
  34.     private static final int MIN_SELECT_SIZE = 15;

  35.     /** A {@link PivotingStrategy} used for pivoting.  */
  36.     private final PivotingStrategy pivotingStrategy;

  37.     /**
  38.      * Constructor with default {@link PivotingStrategy#MEDIAN_OF_3 median of 3}
  39.      * pivoting strategy.
  40.      */
  41.     public KthSelector() {
  42.         this.pivotingStrategy = PivotingStrategy.MEDIAN_OF_3;
  43.     }

  44.     /**
  45.      * Constructor with specified pivoting strategy
  46.      *
  47.      * @param pivotingStrategy pivoting strategy to use
  48.      * @throws NullArgumentException when pivotingStrategy is null
  49.      */
  50.     public KthSelector(final PivotingStrategy pivotingStrategy)
  51.         throws NullArgumentException {
  52.         MathUtils.checkNotNull(pivotingStrategy);
  53.         this.pivotingStrategy = pivotingStrategy;
  54.     }

  55.     /** Get the pivoting strategy.
  56.      * @return pivoting strategy
  57.      */
  58.     public PivotingStrategy getPivotingStrategy() {
  59.         return pivotingStrategy;
  60.     }

  61.     /**
  62.      * Select K<sup>th</sup> value in the array.
  63.      *
  64.      * @param work work array to use to find out the K<sup>th</sup> value
  65.      * @param pivotsHeap cached pivots heap that can be used for efficient estimation
  66.      * @param k the index whose value in the array is of interest
  67.      * @return K<sup>th</sup> value
  68.      */
  69.     public double select(final double[] work, final int[] pivotsHeap, final int k) {
  70.         int begin = 0;
  71.         int end = work.length;
  72.         int node = 0;
  73.         final boolean usePivotsHeap = pivotsHeap != null;
  74.         while (end - begin > MIN_SELECT_SIZE) {
  75.             final int pivot;

  76.             if (usePivotsHeap && node < pivotsHeap.length &&
  77.                     pivotsHeap[node] >= 0) {
  78.                 // the pivot has already been found in a previous call
  79.                 // and the array has already been partitioned around it
  80.                 pivot = pivotsHeap[node];
  81.             } else {
  82.                 // select a pivot and partition work array around it
  83.                 pivot = partition(work, begin, end, pivotingStrategy.pivotIndex(work, begin, end));
  84.                 if (usePivotsHeap && node < pivotsHeap.length) {
  85.                     pivotsHeap[node] = pivot;
  86.                 }
  87.             }

  88.             if (k == pivot) {
  89.                 // the pivot was exactly the element we wanted
  90.                 return work[k];
  91.             } else if (k < pivot) {
  92.                 // the element is in the left partition
  93.                 end  = pivot;
  94.                 node = FastMath.min(2 * node + 1, usePivotsHeap ? pivotsHeap.length : end);
  95.             } else {
  96.                 // the element is in the right partition
  97.                 begin = pivot + 1;
  98.                 node  = FastMath.min(2 * node + 2, usePivotsHeap ? pivotsHeap.length : end);
  99.             }
  100.         }
  101.         Arrays.sort(work, begin, end);
  102.         return work[k];
  103.     }

  104.     /**
  105.      * Partition an array slice around a pivot.Partitioning exchanges array
  106.      * elements such that all elements smaller than pivot are before it and
  107.      * all elements larger than pivot are after it.
  108.      *
  109.      * @param work work array
  110.      * @param begin index of the first element of the slice of work array
  111.      * @param end index after the last element of the slice of work array
  112.      * @param pivot initial index of the pivot
  113.      * @return index of the pivot after partition
  114.      */
  115.     private int partition(final double[] work, final int begin, final int end, final int pivot) {

  116.         final double value = work[pivot];
  117.         work[pivot] = work[begin];

  118.         int i = begin + 1;
  119.         int j = end - 1;
  120.         while (i < j) {
  121.             while (i < j && Double.compare(work[j], value) > 0) {
  122.                 --j;
  123.             }
  124.             while (i < j && Double.compare(work[i], value) < 0) {
  125.                 ++i;
  126.             }

  127.             if (i < j) {
  128.                 final double tmp = work[i];
  129.                 work[i++] = work[j];
  130.                 work[j--] = tmp;
  131.             }
  132.         }

  133.         if (i >= end || Double.compare(work[i], value) > 0) {
  134.             --i;
  135.         }
  136.         work[begin] = work[i];
  137.         work[i] = value;
  138.         return i;
  139.     }
  140. }