How to pick exactly k
bits from a Java BitSet of length m
with n
bits turned on, where k≤n≤m
?
Example input: m=2
How about finding n
positions of all set bits and placing them in a collection as the first step, and them choosing k
positions from that collection randomly?
You could scan the set from the first bit to the last, and apply reservoir sampling to the bits that are set.
The algorithm has O(m)
time complexity, and requires O(k)
memory.
If the constraints allow it you can solve the task by:
Construct a List
holding all the set bits indexes. Do Collections#shuffle
on it. Choose the first k
indexes from the shuffled list.
EDIT As per the comments this algorithm can be inefficient if k
is really small, whilst n
is big. Here is an alternative: generate k
random, different numbers in the interval [0, n]
. If in the generation of a number the number is already present in the set of chosen indices, do the chaining approach: that is increase the number by 1 until you get a number that is not yet present in the set. Finally the generated indices are those that you choose amongst the set bits.
If n
is much larger than k
, you can just pare down the Fisher-Yates shuffle algorithm to stop after you've chosen as many as you need:
private static Random rand = new Random();
public static BitSet chooseBits(BitSet b, int k) {
int n = b.cardinality();
int[] indices = new int[n];
// collect indices:
for (int i = 0, j = 0; i < n; i++) {
j=b.nextSetBit(j);
indices[i] =j++;
}
// create returning set:
BitSet ret = new BitSet(b.size());
// choose k bits:
for (int i = 0; i<k; i++) {
//The first n-i elements are still available.
//We choose one:
int pick = rand.nextInt(n-i);
//We add it to our returning set:
ret.set(indices[pick]);
//Then we replace it with the current (n-i)th element
//so that, when i is incremented, the
//first n-i elements are still available:
indices[pick] = indices[n-i-1];
}
return ret;
}