从Java BitSet中随机选取n个k位
如何从n
位开启的长度为m
的Java BitSet中精确选取k
位,其中k≤n≤m
?
输入示例: m=20, n=11
输出示例: k=3
天真的做法
选择一个随机数0≤ i ≤ m-1
如果它在输入上打开而未在输出上打开,则在输出中将其打开,直到输出中的k
位打开。
当n
远小于m
时,这种方法失败。 还有其他想法吗?
您可以从第一位扫描到最后一位,并将储存采样应用于设置的位。
该算法具有O(m)
时间复杂度,并且需要O(k)
存储器。
如何找到所有设置位的n
位置并将它们作为第一步放入集合中,然后从该集合中随机选择k
位置?
如果约束允许,您可以通过以下方式解决任务:
构造一个包含所有设置位索引的List
。 Collections#shuffle
就可以了。 从随机列表中选择前k
索引。
编辑根据评论,如果k
非常小,则该算法效率低,而n
很大。 这是一个替代方案:在区间[0, n]
生成k
随机的不同数字。 如果在生成数字时,数字已经存在于所选索引集中,则执行链接方法:即将数字增加1,直到得到集合中尚未出现的数字。 最后生成的索引是您在设置位中选择的索引。
如果n
比k
大得多,你可以在选择尽可能多的算法后减去Fisher-Yates shuffle算法:
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