我觉得应该有一个可用的库来更简单地做两件事,A)找到一个数组的模式,在双精度的情况下,B)优雅地降低精度,直到你达到一个特定的频率。
想象一下像这样的数组:
double[] a = {1.12, 1.15, 1.13, 2.0, 3.4, 3.44, 4.1, 4.2, 4.3, 4.4};
如果我正在寻找3的频率,那么它将从2个小数位到1个小数,最后返回1.1作为我的模式。如果我的频率要求为4,它将返回4作为我的模式。
我确实有一组代码按照我想要的方式运行,并返回我期望的内容,但我觉得应该有更有效的方法来实现这一点,或者现有的库可以帮助我做到相同。附上我的代码,我对我应该采取的不同方法的想法/评论感兴趣....我列出了迭代,以限制精度降低的程度。
public static double findMode(double[] r, int frequencyReq)
{
double mode = 0d;
int frequency = 0;
int iterations = 4;
HashMap<Double, BigDecimal> counter = new HashMap<Double, BigDecimal>();
while(frequency < frequencyReq && iterations > 0){
String roundFormatString = "#.";
for(int j=0; j<iterations; j++){
roundFormatString += "#";
}
DecimalFormat roundFormat = new DecimalFormat(roundFormatString);
for(int i=0; i<r.length; i++){
double element = Double.valueOf(roundFormat.format(r[i]));
if(!counter.containsKey(element))
counter.put(element, new BigDecimal(0));
counter.put(element,counter.get(element).add(new BigDecimal(1)));
}
for(Double key : counter.keySet()){
if(counter.get(key).compareTo(new BigDecimal(frequency))>0){
mode = key;
frequency = counter.get(key).intValue();
log.debug("key: " + key + " Count: " + counter.get(key));
}
}
iterations--;
}
return mode;
}
修改
另一种重新解释这个问题的方法,根据Paulo的评论:目标是找到一个数字,在邻域中至少有frequency
个数组元素,邻域的半径尽可能小。
答案 0 :(得分:1)
我认为您的代码没有任何问题,我怀疑您会找到一个可以执行特定操作的库。但是,如果您仍然希望使用更多OOP方法来重用Java集合来解决这个问题,那么另一种方法是:
VariableDecimal(double d,int ndecimals)
一样构造函数。equals
和hashCode
。 equals
的实施将测试VariableDecimal
的两个实例是否相同,并考虑值d
和小数位数。 hashCode
可以简单地将d*exp(10,ndecimals)
转换为Integer。在您的逻辑中使用HashMaps
,以便他们重复使用您的对象:
HashMap<VariableDecimal, AtomicInteger> counters = new HashMap<VariableDecimal, AtomicInteger>();
for (double d : a) {
VariableDecimal vd = new VariableDecimal(d,ndecimals);
if (counters.get(vd)!=null)
counters.set(vd,new AtomicInteger(0));
counters.get(vd).incrementAndGet();
}
/* at the end of this loop counters should hold a map with frequencies of
each double for the selected precision so that you can simply traverse and
get the max */
这段代码没有显示递减小数位数的迭代,这是微不足道的。
答案 1 :(得分:1)
这是对重新提出的问题的解决方案:
目标是找到一个数字,其中邻域中至少有
frequency
个数组元素,邻域的半径尽可能小。
(我可以自由切换输入数组中1.15
和1.13
的顺序。)
基本思想是:我们已经对输入进行了排序(即相邻元素是连续的),并且我们知道在邻域中需要多少元素。所以我们在这个数组上循环一次,测量左边元素和元素frequency
元素之间的距离。它们之间是frequency
元素,所以这形成了一个邻域。然后我们简单地采取最小距离。 (我的方法有一种复杂的方式来返回结果,你可能想要做得更好。)
这并不完全等同于您的原始问题(不能通过固定的数字步骤工作),但也许这更像您真正想要的: - )
但是,你必须找到一种更好的格式化结果的方法。
package de.fencing_game.paul.examples;
import java.util.Arrays;
/**
* searching of dense points in a distribution.
*
* Inspired by http://stackoverflow.com/questions/5329628/finding-a-mode-with-decreasing-precision.
*/
public class InpreciseMode {
/** our input data, should be sorted ascending. */
private double[] data;
public InpreciseMode(double ... data) {
this.data = data;
}
/**
* searchs the smallest neighbourhood (by diameter) which
* contains at least minSize elements.
*
* @return an array of two arrays:
* { { the middle point of the neighborhood,
* the diameter of the neighborhood },
* all the elements of the neigborhood }
*
* TODO: better return an object of a class encapsuling these.
*/
public double[][] findSmallNeighbourhood(int minSize) {
int currentLeft = -1;
int currentRight = -1;
double currentMinDiameter = Double.POSITIVE_INFINITY;
for(int i = 0; i + minSize-1 < data.length; i++) {
double diameter = data[i+minSize-1] - data[i];
if(diameter < currentMinDiameter) {
currentMinDiameter = diameter;
currentLeft = i;
currentRight = i + minSize-1;
}
}
return
new double[][] {
{
(data[currentRight] + data[currentLeft])/2.0,
currentMinDiameter
},
Arrays.copyOfRange(data, currentLeft, currentRight+1)
};
}
public void printSmallNeighbourhoods() {
for(int frequency = 2; frequency <= data.length; frequency++) {
double[][] found = findSmallNeighbourhood(frequency);
System.out.printf("There are %d elements in %f radius "+
"around %f:%n %s.%n",
frequency, found[0][1]/2, found[0][0],
Arrays.toString(found[1]));
}
}
public static void main(String[] params) {
InpreciseMode m =
new InpreciseMode(1.12, 1.13, 1.15, 2.0, 3.4, 3.44, 4.1,
4.2, 4.3, 4.4);
m.printSmallNeighbourhoods();
}
}
输出
There are 2 elements in 0,005000 radius around 1,125000:
[1.12, 1.13].
There are 3 elements in 0,015000 radius around 1,135000:
[1.12, 1.13, 1.15].
There are 4 elements in 0,150000 radius around 4,250000:
[4.1, 4.2, 4.3, 4.4].
There are 5 elements in 0,450000 radius around 3,850000:
[3.4, 3.44, 4.1, 4.2, 4.3].
There are 6 elements in 0,500000 radius around 3,900000:
[3.4, 3.44, 4.1, 4.2, 4.3, 4.4].
There are 7 elements in 1,200000 radius around 3,200000:
[2.0, 3.4, 3.44, 4.1, 4.2, 4.3, 4.4].
There are 8 elements in 1,540000 radius around 2,660000:
[1.12, 1.13, 1.15, 2.0, 3.4, 3.44, 4.1, 4.2].
There are 9 elements in 1,590000 radius around 2,710000:
[1.12, 1.13, 1.15, 2.0, 3.4, 3.44, 4.1, 4.2, 4.3].
There are 10 elements in 1,640000 radius around 2,760000:
[1.12, 1.13, 1.15, 2.0, 3.4, 3.44, 4.1, 4.2, 4.3, 4.4].