关于算法:从Java BitSet中随机选取n个k位

Randomly pick k bits out of n from a Java BitSet

如何从EDCOX1长度为1的Java位集准确地选择EDCOX1×0位,EDCX1 2位开启,其中EDCOX1为3?

示例输入:m=20, n=11enter image description here

示例输出:k=3。氧化镁

天真的方法

选择一个随机数0≤ i ≤ m-1。如果它在输入端打开而不在输出端打开,则在输出端打开它,直到k位在输出端打开。

nm小得多时,这种方法就失败了。还有其他想法吗?


您可以从第一个位扫描到最后一个位,并对设置的位应用水库采样。

该算法具有O(m)时间复杂度,需要O(k)内存。


如果nk大得多,那么在您选择了所需的任意数量后,您可以将fisher-yates shuffle算法简化为stop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}


如何找到所有集合位的n位置并将其作为第一步放入集合中,然后从集合中随机选择k位置?


如果约束允许,可以通过以下方式解决任务:

构造一个包含所有设置位索引的List。在上面做Collections#shuffle。从无序排列的列表中选择第一个k索引。

如果k非常小,而n很大,则根据注释编辑此算法可能效率很低。这里有一个替代方法:生成k随机数,在区间[0, n]中有不同的数字。如果在数字的生成过程中,数字已经存在于所选索引集中,请执行链接方法:即将数字增加1,直到得到一个尚未存在于该集合中的数字。最后,生成的索引是您在集合位中选择的索引。