我正在尝试编写一个基于随机生成的指令集的简单虚拟机。在C中,生成包含 N位的随机位掩码的最佳方法是什么(与生成随机整数不同,因为不能保证在其中设置N位) )。我需要这个适用于16位和32位整数。
修改:必须设置完全 N位。 完全即可。不多。而不是更少。 完全 N位设置。它不一定非常安全,也不必从宇宙噪声中获得它的熵。它必须是伪随机的。
这就是我实际想要实现的目标:
uint32_t rand_bits_32(size_t reqBits)
{
/* blah */
}
uint16_t rand_bits_16(size_t reqBits)
{
/* blah */
}
extern char *int2bin(uint32_t n, char *buf);
uint16_t gen_mask_16_excl_32(uint32_t* exclude, size_t exclude_count, size_t bits_required)
{
uint32_t ret = 0;
while (1) {
bool has = false;
ret = (uint32_t)rand_bits_16(bits_required);
for (size_t i = 0; i < exclude_count; i++) {
if (ret & (uint32_t)exclude[i]) {
has = true;
break;
}
}
if (!has) {
break;
}
has = false;
}
return ret;
}
uint32_t gen_mask_32(uint32_t* exclude, size_t exclude_count, size_t bits_required)
{
uint32_t ret = 0;
while (1) {
bool has = false;
ret = rand_bits_32(bits_required);
for (int i = 0; i < exclude_count; i++) {
if (ret & (uint32_t)exclude[i]) {
has = true;
break;
}
}
if (!has)
break;
has = false;
}
return ret;
}
我生成随机位,然后对现有的位掩码强制AND
,直到没有位匹配,因此我可以生成具有N个位的位掩码,并且没有任何位与另一个位相同位掩码。是的,这段代码非常糟糕,在x86_64上打破了。
答案 0 :(得分:4)
我根据Steve Emmerson的想法松散地写了一个C99实现。 你可以看到它running at ideone。
除非randto()
和shuffle()
出现任何错误,否则应该按照自己的意愿行事。
#include <stdint.h>
#include <stdlib.h>
static int randto(int n) {
int r;
int maxrand = (RAND_MAX / n) * n;
do r = rand(); while (r >= maxrand);
return r % n;
}
static void shuffle(unsigned *x, size_t n) {
while (--n) {
size_t j = randto(n + 1);
unsigned tmp = x[n];
x[n] = x[j];
x[j] = tmp;
}
}
uint16_t nrand16(int n) {
uint16_t v = 0;
static unsigned pos[16] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15};
shuffle(pos, 16);
while (n--) v |= (1U << pos[n]);
return v;
}
uint32_t nrand32(int n) {
uint32_t v = 0;
static unsigned pos[32] = { 0, 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, 26, 27, 28, 29, 30, 31};
shuffle(pos, 32);
while (n--) v |= (1U << pos[n]);
return v;
}
答案 1 :(得分:1)
我最终选择了自己的实现。它似乎有效。
static bool prob(double probability)
{
return rand() < probability * ((double)RAND_MAX + 1.0);
}
uint32_t count_set_bits(uint32_t i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
uint32_t gen_mask_excl_32(size_t bits, uint32_t* exclude, size_t exclude_count, size_t bits_required)
{
uint32_t excl_mask = 0;
size_t mask_bit_count;
size_t rem_bit_count;
uint32_t out_mask = 0;
if (exclude_count == 0) {
/* pass */
if (exclude != NULL) {
abort();
}
}
else {
for (size_t i = 0; i < exclude_count; i++) {
excl_mask |= exclude[i];
}
}
mask_bit_count = count_set_bits(excl_mask);
if (mask_bit_count == bits) {
/* overflow! */
abort();
}
rem_bit_count = bits - mask_bit_count;
retry:
for (size_t i = 0; i < bits; i++) {
unsigned re;
if (( 1 << i ) & excl_mask || ( 1 << i ) & out_mask) {
/* bit already set, skip */
continue;
}
re = prob((double)1 / (double)rem_bit_count);
if (re) {
out_mask = ( 1 << i ) | out_mask;
bits_required--;
}
if (bits_required == 0) {
break;
}
}
/* still stuff left */
if (bits_required) {
goto retry;
}
return out_mask;
}
uint16_t gen_mask_16_excl_32(uint32_t* exclude, size_t exclude_count, size_t bits_required)
{
return (uint16_t)gen_mask_excl_32(16, exclude, exclude_count, bits_required);
}
uint32_t gen_mask_32(uint32_t* exclude, size_t exclude_count, size_t bits_required)
{
return (uint32_t)gen_mask_excl_32(32, exclude, exclude_count, bits_required);
}
uint32_t rand_bits_32(size_t reqBits)
{
return gen_mask_32(NULL, 0, reqBits);
}
uint16_t rand_bits_16(size_t reqBits)
{
return gen_mask_16_excl_32(NULL, 0, reqBits);
}
答案 2 :(得分:0)
这样的事情可能会起作用
#include <stdlib.h>
#include <string.h>
unsigned long set_bits(
unsigned size, /** [in] Size of the integer in bits: 16, 32 */
const unsigned nbits)/** [in] Number of bits to be set */
{
unsigned pos[nbits];
unsigned long result = 0;
srand(time(NULL));
for (unsigned i = 0; i < size; i++)
pos[i] = i;
for (unsigned i = 0; i < nbits; i++) {
unsigned j = rand()%size--;
result |= 1 << pos[j];
(void)memmov(pos+j, pos+j+1, size-j);
}
return result;
}
我没有测试过这个。
答案 3 :(得分:0)
如果RAN32()返回带随机位的随机uint32_t,则下面的代码应该完成工作。 这种功能应由您通常的随机数发生器提供。
uint32_t fixed_nb_bits(int b)
{
if ( b > 31 )
return ~0;
uint32_t n = 0;
while ( b > 0 )
{
uint32_t x = 1 << ( RAN32() % 32 );
if (!( n & x ))
{
n += x;
--b;
}
}
return n;
}
然而,这段代码对于(b> 16)并不是最佳的,其中最好只反转:
~fixed_nb_bits(32-b)