我需要生成一个随机数,但需要从具有相同设置位数的二进制数集中选择。例如。选择一个正好设置2位的随机字节值...
00000000 - no
00000001 - no
00000010 - no
00000011 - YES
00000100 - no
00000101 - YES
00000110 - YES
...
=> Set of possible numbers 3, 5, 6...
请注意,这是一组简化的数字。更多地考虑“选择一个正确的40位设置的随机64位数字”。该组中的每个数字必须同样可能出现。
答案 0 :(得分:6)
从所有位位置进行随机选择,然后设置这些位。
Python中的示例:
def random_bits(word_size, bit_count):
number = 0
for bit in random.sample(range(word_size), bit_count):
number |= 1 << bit
return number
运行上述10次的结果:
0xb1f69da5cb867efbL
0xfceff3c3e16ea92dL
0xecaea89655befe77L
0xbf7d57a9b62f338bL
0x8cd1fee76f2c69f7L
0x8563bfc6d9df32dfL
0xdf0cdaebf0177e5fL
0xf7ab75fe3e2d11c7L
0x97f9f1cbb1f9e2f8L
0x7f7f075de5b73362L
答案 1 :(得分:5)
我找到了一个优雅的解决方案:随机二分法。
理念是平均的:
使用gcc编译的C代码(具有__builtin_popcountll):
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/// Return a random number, with nb_bits bits set out of the width LSB
uint64_t random_bits(uint8_t width, uint8_t nb_bits)
{
assert(nb_bits <= width);
assert(width <= 64);
uint64_t x_min = 0;
uint64_t x_max = width == 64 ? (uint64_t)-1 : (1UL<<width)-1;
int n = 0;
while (n != nb_bits)
{
// generate a random value of at least width bits
uint64_t x = random();
if (width > 31)
x ^= random() << 31;
if (width > 62)
x ^= random() << 33;
x = x_min | (x & x_max); // x_min is a subset of x, which is a subset of x_max
n = __builtin_popcountll(x);
printf("x_min = 0x%016lX, %d bits\n", x_min, __builtin_popcountll(x_min));
printf("x_max = 0x%016lX, %d bits\n", x_max, __builtin_popcountll(x_max));
printf("x = 0x%016lX, %d bits\n\n", x, n);
if (n > nb_bits)
x_max = x;
else
x_min = x;
}
return x_min;
}
通常需要少于10个循环来达到所请求的位数(幸运的是,它可能需要2或3个循环)。拐角情况(nb_bits = 0,1,宽度-1,宽度)即使特殊情况更快也能正常工作。
结果示例:
x_min = 0x0000000000000000, 0 bits
x_max = 0x1FFFFFFFFFFFFFFF, 61 bits
x = 0x1492717D79B2F570, 33 bits
x_min = 0x0000000000000000, 0 bits
x_max = 0x1492717D79B2F570, 33 bits
x = 0x1000202C70305120, 14 bits
x_min = 0x0000000000000000, 0 bits
x_max = 0x1000202C70305120, 14 bits
x = 0x0000200C10200120, 7 bits
x_min = 0x0000200C10200120, 7 bits
x_max = 0x1000202C70305120, 14 bits
x = 0x1000200C70200120, 10 bits
x_min = 0x1000200C70200120, 10 bits
x_max = 0x1000202C70305120, 14 bits
x = 0x1000200C70201120, 11 bits
x_min = 0x1000200C70201120, 11 bits
x_max = 0x1000202C70305120, 14 bits
x = 0x1000200C70301120, 12 bits
width = 61, nb_bits = 12, x = 0x1000200C70301120
当然,你需要一个好的技巧。否则你可能面临无限循环。
答案 2 :(得分:4)
假设要设置的位数是b,字大小是w。我将创建一个长度为w的向量v,其中第一个b值设置为1,其余值设置为0.然后只是随机播放v。
答案 3 :(得分:2)
这是另一种选择,它在实践中非常简单且相当快。
choose a bit at random
if it is already set
do nothing
else
set it
increment count
end if
重复直到count等于你想要设置的位数。
当你想要设置的位数(称为k
)超过字长的一半(称之为N
)时,这只会很慢。在这种情况下,请使用算法来设置N
- k
位,然后翻转结果中的所有位。
我打赌这里的预期运行时间非常好,虽然我现在太懒惰/愚蠢无法计算它。但是我可以将它限制为小于2 * k
...预计获得“头部”的硬币翻转次数为2次,并且每次迭代都有超过1/2次成功的机会。 / p>
答案 4 :(得分:1)
如果您没有Python random.sample
的便利,可以使用经典的顺序采样算法在C中执行此操作:
unsigned long k_bit_helper(int n, int k, unsigned long bit, unsigned long accum) {
if !(n && k)
return accum;
if (k > rand() % n)
return k_bit_helper(n - 1, k - 1, bit + bit, accum + bit);
else
return k_bit_helper(n - 1, k, bit + bit, accum);
}
unsigned long random_k_bits(int k) {
return k_bit_helper(64, k, 1, 0);
}
上述成本将由生成随机数的成本决定(在其他解决方案中也是如此)。如果通过批处理有一个好的prng,你可以稍微优化一下:例如,因为你知道随机数将在稳定下降的范围内,你可以获得n
到n-3
的随机数通过获取0..(n * (n - 1) * (n - 2) * (n - 3))
范围内的随机数,然后提取单个随机数:
r = randint(0, n * (n - 1) * (n - 2) * (n - 3) - 1);
rn = r % n; r /= n
rn1 = r % (n - 1); r /= (n - 1);
rn2 = r % (n - 2); r /= (n - 2);
rn3 = r % (n - 3); r /= (n - 3);
n
的最大值大概为64
或2 6 ,因此上述产品的最大值肯定小于2 24 。实际上,如果你使用64位prng,你可以从中提取多达10个随机数。但是,除非您知道使用的prng产生独立的随机位,否则不要这样做。
答案 5 :(得分:1)
我有另一个基于枚举的建议:选择1和n之间的随机数i选择k,并生成第i个组合。例如,对于n = 6,k = 3,20种组合是:
000111
001011
010011
100011
001101
010101
100101
011001
101001
110001
001110
010110
100110
011010
101010
110010
011100
101100
110100
111000
假设我们随机选择组合编号7.我们首先检查它在最后一个位置是否有1:它有,因为前10个(5个选择2个)组合有。然后我们递归检查剩余的位置。这是一些C ++代码:
word ithCombination(int n, int k, word i) {
// i is zero-based
word x = 0;
word b = 1;
while (k) {
word c = binCoeff[n - 1][k - 1];
if (i < c) {
x |= b;
--k;
} else {
i -= c;
}
--n;
b <<= 1;
}
return x;
}
word randomKBits(int k) {
word i = randomRange(0, binCoeff[BITS_PER_WORD][k] - 1);
return ithCombination(BITS_PER_WORD, k, i);
}
为了快速,我们在binCoeff
中使用预先计算的二项式系数。函数randomRange
返回两个边界之间的随机整数(包含)。
我做了一些时间安排(source)。使用C ++ 11默认随机数生成器,大部分时间用于生成随机数。然后这个解决方案是最快的,因为它使用可能的绝对最小随机位数。如果我使用快速随机数发生器,那么mic006的解决方案是最快的。如果已知k
非常小,则最好随机设置位,直到设置k
为止。
答案 6 :(得分:0)
不完全是算法建议,只是在 JavaScript 中找到了一个非常巧妙的解决方案,可以使用 ArrayBuffer 直接从 Math.random 输出位中获取随机位。
//Swap var out with const and let for maximum performance! I like to use var because of prototyping ease
var randomBitList = function(n){
var floats = Math.ceil(n/64)+1;
var buff = new ArrayBuffer(floats*8);
var floatView = new Float64Array(buff);
var int8View = new Uint8Array(buff);
var intView = new Int32Array(buff);
for(var i = 0; i < (floats-1)*2; i++){
floatView[floats-1] = Math.random();
int8View[(floats-1)*8] = int8View[(floats-1)*8+4];
intView[i] = intView[(floats-1)*2];
}
this.get = function(idx){
var i = idx>>5;//divide by 32
var j = idx%32;
return (intView[i]>>j)&1;
//return Math.random()>0.5?0:1;
};
this.getBitList = function(){
var arr = [];
for(var idx = 0; idx < n; idx++){
var i = idx>>5;//divide by 32
var j = idx%32;
arr[idx] = (intView[i]>>j)&1;
}
return arr;
}
};