Java 不可变数组的近似中值
我需要找到一个double数组的中值(在Java中),而不需要修改它(因此选择被取消)或分配大量新内存。我也不想找到精确的中位数,但在10%以内就可以了(因此,如果中位数将排序数组拆分为40%-60%,就可以了) 我如何才能有效地实现这一点 考虑到rfreak、ILMTitan和Peter的建议,我编写了以下代码:Java 不可变数组的近似中值,java,arrays,median,Java,Arrays,Median,我需要找到一个double数组的中值(在Java中),而不需要修改它(因此选择被取消)或分配大量新内存。我也不想找到精确的中位数,但在10%以内就可以了(因此,如果中位数将排序数组拆分为40%-60%,就可以了) 我如何才能有效地实现这一点 考虑到rfreak、ILMTitan和Peter的建议,我编写了以下代码: public static double median(double[] array) { final int smallArraySize = 5000; fina
public static double median(double[] array) {
final int smallArraySize = 5000;
final int bigArraySize = 100000;
if (array.length < smallArraySize + 2) { // small size, so can just sort
double[] arr = array.clone();
Arrays.sort(arr);
return arr[arr.length / 2];
} else if (array.length > bigArraySize) { // large size, don't want to make passes
double[] arr = new double[smallArraySize + 1];
int factor = array.length / arr.length;
for (int i = 0; i < arr.length; i++)
arr[i] = array[i * factor];
return median(arr);
} else { // average size, can sacrifice time for accuracy
final int buckets = 1000;
final double desiredPrecision = .005; // in percent
final int maxNumberOfPasses = 10;
int[] histogram = new int[buckets + 1];
int acceptableMin, acceptableMax;
double min, max, range, scale,
medianMin = -Double.MAX_VALUE, medianMax = Double.MAX_VALUE;
int sum, numbers, bin, neighborhood = (int) (array.length * 2 * desiredPrecision);
for (int r = 0; r < maxNumberOfPasses; r ++) { // enter search for number around median
max = -Double.MAX_VALUE; min = Double.MAX_VALUE;
numbers = 0;
for (int i = 0; i < array.length; i ++)
if (array[i] > medianMin && array[i] < medianMax) {
if (array[i] > max) max = array[i];
if (array[i] < min) min = array[i];
numbers ++;
}
if (min == max) return min;
if (numbers <= neighborhood) return (medianMin + medianMax) / 2;
acceptableMin = (int) (numbers * (50d - desiredPrecision) / 100);
acceptableMax = (int) (numbers * (50d + desiredPrecision) / 100);
range = max - min;
scale = range / buckets;
for (int i = 0; i < array.length; i ++)
histogram[(int) ((array[i] - min) / scale)] ++;
sum = 0;
for (bin = 0; bin <= buckets; bin ++) {
sum += histogram[bin];
if (sum > acceptableMin && sum < acceptableMax)
return ((.5d + bin) * scale) + min;
if (sum > acceptableMax) break; // one bin has too many values
}
medianMin = ((bin - 1) * scale) + min;
medianMax = (bin * scale) + min;
for (int i = 0; i < histogram.length; i ++)
histogram[i] = 0;
}
return .5d * medianMin + .5d * medianMax;
}
}
公共静态双中值(双[]数组){
最终整数smallArraySize=5000;
最终int-bigaraysize=100000;
如果(array.lengthbigArraySize){//large size,则不希望进行传递
double[]arr=新的double[smallArraySize+1];
整数因子=array.length/arr.length;
对于(int i=0;imedianMin&&array[i]max)max=array[i];
如果(数组[i]
这里我考虑了数组的大小。如果它很小,那么只需排序并获得真正的中值。如果它非常大,则对其进行采样并获得样本的中值,否则迭代地对值进行装箱,看看中值是否可以缩小到可接受的范围
我对这个代码没有任何问题。如果有人发现它有问题,请告诉我
谢谢。随机选取少量数组元素,然后找到这些元素的中间值。假设您指的是中间值而不是平均值。另外,假设您使用的是相当大的double[],或者内存对于排序副本和执行精确的中位数不会是一个问题 用最少的额外内存开销,你可能会运行一个O(n)算法,这将是一个大概的结果。我会试试这个,看看它有多准确 两次传球 第一次通过找到最小值和最大值。创建一组桶,表示最小值和最大值之间均匀分布的数字范围。进行第二次通过并“计算”每个箱子中的数字数量。然后你应该能够对中位数做出合理的估计。如果使用int[]存储存储桶,那么使用1000个桶只需要4k。数学应该很快 唯一的问题是准确性,我认为您应该能够调整存储桶的数量,使其处于数据集的错误范围内 我相信有比我有更好的数学/统计背景的人可以提供一个精确的大小来获取您要查找的错误范围。1)多少是新内存?它是否排除了数据或数据引用的排序副本 2) 您的数据是否重复(是否有许多不同的值)?如果是,那么您对(1)的回答不太可能引起问题,因为您可以使用查找映射和数组执行某些操作:例如,映射和短数组以及经过适当调整的比较对象 3) “接近平均值”近似值的典型情况更可能是O(n.log(n))。对于病理数据,大多数排序算法仅降级为O(n^2)。此外,假设您能够负担得起排序副本,那么精确的中位数(通常)是O(n.log(n))
4) 随机抽样(a-la dan04)比选择接近平均值的值更可能准确,除非您的分布表现良好。例如,泊松分布和对数正态分布都有不同的平均值;如何从更大的数组中提取N个值 下面的代码显示了查找大型数组的中值所需的时间,然后显示了查找固定大小值选择的中值所需的时间。固定大小的选择具有固定的成本,但随着原始阵列大小的增加,选择会越来越不准确 下面的照片
Avg time 17345 us. median=0.5009231700563378
Avg time 24 us. median=0.5146687617507585
代码
double[] nums = new double[100 * 1000 + 1];
for (int i = 0; i < nums.length; i++) nums[i] = Math.random();
{
int runs = 200;
double median = 0;
long start = System.nanoTime();
for (int r = 0; r < runs; r++) {
double[] arr = nums.clone();
Arrays.sort(arr);
median = arr[arr.length / 2];
}
long time = System.nanoTime() - start;
System.out.println("Avg time " + time / 1000 / runs + " us. median=" + median);
}
{
int runs = 20000;
double median = 0;
long start = System.nanoTime();
for (int r = 0; r < runs; r++) {
double[] arr = new double[301]; // fixed size to sample.
int factor = nums.length / arr.length; // take every nth value.
for (int i = 0; i < arr.length; i++)
arr[i] = nums[i * factor];
Arrays.sort(arr);
median = arr[arr.length / 2];
}
long time = System.nanoTime() - start;
System.out.println("Avg time " + time / 1000 / runs + " us. median=" + median);
}
double[]nums=新的双精度[100*1000+1];
对于(int i=0;i