我需要能够将元素从包含只能访问迭代器的元素m
的地图中逐出。n
我可以简单地迭代字典一次并删除所有具有概率的元素m/n
,但是这可能会驱逐更多或更少的m
项目(尽管删除的项目的预期数量是正确的m
)。
int m = 10;
int n = map.size();
Iterator<K> keys = map.keySet().iterator();
while (keys.hasNext()) {
keys.next();
if (random.nextDouble() < m / (double) n) {
keys.remove();
}
}
我一直在考虑的解决方案是在元素被驱逐后简单地停止驱逐元素m
,并在迭代结束时,如果evicted < m
元素已被驱逐,则m - evicted
在第二次迭代中驱逐剩余的元素。我担心这第二遍在概率上是不正确的。
int m = 10;
int n = size();
int evicted = 0;
outer: while (evicted < m) {
Iterator<K> keys = keySet().iterator();
while (keys.hasNext()) {
keys.next();
if (random.nextDouble() < m / (double) n) {
keys.remove();
if (++evicted == m) {
break outer;
}
}
}
或者,我可以保留一个键列表并通过一次迭代对列表进行存储采样,并删除键列表中的所有m
键,但是我不想被迫使用一些内存开销。此外,使用迭代器删除比通过键删除元素更快(它需要找到存储键的存储桶,然后找到它在列表中的位置)。是否有另一种在线算法我可以使用仅访问迭代器(不创建单独的列表)?
编辑:对于那些感兴趣的人,我找到了一篇论文,详细介绍了如何生成随机分布,以便不需要单独的排序步骤。代码是这样的(截断为整数时可能包含重复项):
int curmax = 1.0;
int[] indices = new int[m];
for (int i = indices.length; i >= 0; i--) {
curmax = curmax * Math.pow(random.nextDouble(), 1 / (double) (i+1));
indices[i] = (int) curmax;
}