对于一个地图,其中键表示一个序列的数量,并且该值计算该数字出现在序列中的频率,java中的算法实现如何计算中位数?
例如:
1,1,2,2,2,2,3,3,3,4,5,6,6,6,7,7
在地图中:
Map<Int,Int> map = ...
map.put(1,2)
map.put(2,4)
map.put(3,3)
map.put(4,1)
map.put(5,1)
map.put(6,3)
map.put(7,2)
double median = calculateMedian(map);
print(median);
会导致:
> print(median);
3
>
所以我要找的是calculateMedian
的java实现。
答案 0 :(得分:5)
线性时间
如果您知道数字的总数(在您的情况下是16),您可以从地图的开头或结尾开始,并总结计数,直到您得到第(n / 2)个元素,或者如果总和是平均值(n / 2)和ceil(n / 2)元素= median的平均值。
如果您不知道总计数,则必须至少完成一次。
次线性时间
如果您可以决定数据结构并且可以进行预处理,请参阅selection algorithm上的维基百科,您甚至可以获得次线性算法。 如果您对数据的分布有所了解,也可以获得次线性时间。
编辑: 因此,假设我们有一个计数序列,我们可以做的是
key -> count
对时,key -> running_total
这会使内存使用量增加一倍,但会为中位数提供O(log n)性能,为total_count提供O(1)。
答案 1 :(得分:4)
使用Guava:
Multiset<Integer> values = TreeMultiset.create();
Collections.addAll(values, 1,1,2,2,2,2,3,3,3,4,5,6,6,6,7,7);
现在你问题的答案是:
return Iterables.get(values, (values.size() - 1) / 2);
<强>真。就是这样。(或者检查尺寸是否均匀并平均两个中心值,确切地说是它。)
如果计数特别大,使用多重集entrySet
并保持运行总和会更快,但最简单的方法通常很好。
答案 2 :(得分:2)
SortedMap
,即TreeMap
答案 3 :(得分:1)
对于简单但可能不那么高效的算法,我会这样做:
<强> 1。将地图展开到列表中。
实际说法:遍历地图并将键'value-times'添加到新列表中。最后对列表进行排序。
//...
List<Integer> field = new ArrayList<Integer>();
for (Integer key:map) {
for (int i = 0; i < map.get(key); i++) {
field.add(key);
}
}
Collections.sort(field);
<强> 2。计算中位数
现在你必须实现一个方法int calculateMedian(List<Integer> sorted)
。这取决于您需要的中位数类型。如果它只是样本中位数,则结果是中间值(对于具有奇数个元素的列表)或两个中间值的平均值(对于具有偶数长度的列表)。请注意,列表需要排序!
(参考:Sample Median / wikipedia)
好的,好的,即使克里斯没有提到效率,这里有一个想法如何计算样本中位数(!)而不扩展地图...
Set<Integer> sortedKeys = new TreeSet<Integer>(map.keySet()); // just to be sure ;)
Integer median = null; // Using Integer to have a 'invalid/not found/etc' state
int total = 0;
for (Integer key:sortedKeys) {
total += map.get(key);
}
if (isOddNumber(total)) { // I don't have to implement everything, do I?
int counter = total / 2; // index starting with 0
for (Integer key:sortedKeys) {
middleMost -= map.get(key);
if (counter < 0) {
// the sample median was in the previous bin
break;
}
median = key;
}
} else {
int lower = total/2;
int upper = lower + 1;
for (Integer key:sortedKeys) {
lower -= map.get(key);
upper -= map.get(key);
if (lower < 0 && upper < 0) {
// both middlemost values are in the same bin
break;
} else (lower < 0 || upper < 0) {
// lower is in the previous, upper in the actual bin
median = (median + key) / 2; // now we need the average
break;
}
median = key;
}
}
(我手边没有编译器 - 如果它有很多语法错误,请将其视为伪代码;)