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 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.util; 23 24 import java.io.Serializable; 25 import java.util.Arrays; 26 27 import org.hipparchus.exception.NullArgumentException; 28 29 30 /** 31 * A Simple K<sup>th</sup> selector implementation to pick up the 32 * K<sup>th</sup> ordered element from a work array containing the 33 * input numbers. 34 */ 35 public class KthSelector implements Serializable { 36 37 /** Serializable UID. */ 38 private static final long serialVersionUID = 20140713L; 39 40 /** Minimum selection size for insertion sort rather than selection. */ 41 private static final int MIN_SELECT_SIZE = 15; 42 43 /** A {@link PivotingStrategy} used for pivoting. */ 44 private final PivotingStrategy pivotingStrategy; 45 46 /** 47 * Constructor with default {@link PivotingStrategy#MEDIAN_OF_3 median of 3} 48 * pivoting strategy. 49 */ 50 public KthSelector() { 51 this.pivotingStrategy = PivotingStrategy.MEDIAN_OF_3; 52 } 53 54 /** 55 * Constructor with specified pivoting strategy 56 * 57 * @param pivotingStrategy pivoting strategy to use 58 * @throws NullArgumentException when pivotingStrategy is null 59 */ 60 public KthSelector(final PivotingStrategy pivotingStrategy) 61 throws NullArgumentException { 62 MathUtils.checkNotNull(pivotingStrategy); 63 this.pivotingStrategy = pivotingStrategy; 64 } 65 66 /** Get the pivoting strategy. 67 * @return pivoting strategy 68 */ 69 public PivotingStrategy getPivotingStrategy() { 70 return pivotingStrategy; 71 } 72 73 /** 74 * Select K<sup>th</sup> value in the array. 75 * 76 * @param work work array to use to find out the K<sup>th</sup> value 77 * @param pivotsHeap cached pivots heap that can be used for efficient estimation 78 * @param k the index whose value in the array is of interest 79 * @return K<sup>th</sup> value 80 */ 81 public double select(final double[] work, final int[] pivotsHeap, final int k) { 82 int begin = 0; 83 int end = work.length; 84 int node = 0; 85 final boolean usePivotsHeap = pivotsHeap != null; 86 while (end - begin > MIN_SELECT_SIZE) { 87 final int pivot; 88 89 if (usePivotsHeap && node < pivotsHeap.length && 90 pivotsHeap[node] >= 0) { 91 // the pivot has already been found in a previous call 92 // and the array has already been partitioned around it 93 pivot = pivotsHeap[node]; 94 } else { 95 // select a pivot and partition work array around it 96 pivot = partition(work, begin, end, pivotingStrategy.pivotIndex(work, begin, end)); 97 if (usePivotsHeap && node < pivotsHeap.length) { 98 pivotsHeap[node] = pivot; 99 } 100 } 101 102 if (k == pivot) { 103 // the pivot was exactly the element we wanted 104 return work[k]; 105 } else if (k < pivot) { 106 // the element is in the left partition 107 end = pivot; 108 node = FastMath.min(2 * node + 1, usePivotsHeap ? pivotsHeap.length : end); 109 } else { 110 // the element is in the right partition 111 begin = pivot + 1; 112 node = FastMath.min(2 * node + 2, usePivotsHeap ? pivotsHeap.length : end); 113 } 114 } 115 Arrays.sort(work, begin, end); 116 return work[k]; 117 } 118 119 /** 120 * Partition an array slice around a pivot.Partitioning exchanges array 121 * elements such that all elements smaller than pivot are before it and 122 * all elements larger than pivot are after it. 123 * 124 * @param work work array 125 * @param begin index of the first element of the slice of work array 126 * @param end index after the last element of the slice of work array 127 * @param pivot initial index of the pivot 128 * @return index of the pivot after partition 129 */ 130 private int partition(final double[] work, final int begin, final int end, final int pivot) { 131 132 final double value = work[pivot]; 133 work[pivot] = work[begin]; 134 135 int i = begin + 1; 136 int j = end - 1; 137 while (i < j) { 138 while (i < j && Double.compare(work[j], value) > 0) { 139 --j; 140 } 141 while (i < j && Double.compare(work[i], value) < 0) { 142 ++i; 143 } 144 145 if (i < j) { 146 final double tmp = work[i]; 147 work[i++] = work[j]; 148 work[j--] = tmp; 149 } 150 } 151 152 if (i >= end || Double.compare(work[i], value) > 0) { 153 --i; 154 } 155 work[begin] = work[i]; 156 work[i] = value; 157 return i; 158 } 159 }