イテレータにのみアクセスできる要素をm
含むマップから要素を削除できる必要があります。n
簡単に言えば、辞書を1回繰り返して、すべての要素を確率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
要素の削除を停止し、反復の最後に、要素が削除された場合は、2回目の反復でevicted < m
残りの要素を削除することです。m - evicted
この2回目のパスが確率的に正しくないのではないかと心配しています。
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;
}
}
}
または、キーのリストを保持し、1回の反復でリストをリザーバーサンプリングし、キーのリスト内のすべてのキーを削除することもできますが、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;
}