【问题标题】:Generate a random integer with N bits?生成一个 N 位的随机整数?
【发布时间】:2013-03-28 20:45:19
【问题描述】:

我正在尝试编写一个基于随机生成的指令集的简单虚拟机。在 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 上会中断。

【问题讨论】:

  • 当你说“N bits”时,你的意思是“N bits set”吗?
  • @SteveEmmerson 好点,我完全忘记了这种可能性。
  • 是的,当我说“N 位”时,我的意思是设置了 N 位。
  • 你可以尝试使用加密安全的伪随机生成器,谷歌一下。
  • 我不想要安全性或极端随机性。只要是随机的,我就没事。但它必须设置 N 位。不大于N且不小于N。

标签: c bit-manipulation


【解决方案1】:

我根据 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;
}

【讨论】:

  • “停止工作”是什么意思?你电脑死机了?函数返回一个数字设置错误的数字?经过多少个周期?我在我的计算机上对 20,000,000 个数字进行了测试,它似乎可以正常工作。
【解决方案2】:

这样的事情可能会奏效

#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;
}

我还没有测试过。

【讨论】:

  • unsigned pos[nbits] 会溢出,因为 size 大于 nbits。
【解决方案3】:

我最终选择了自己的实现。它似乎有效。

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);
}

【讨论】:

    【解决方案4】:

    如果 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)
    

    【讨论】:

      猜你喜欢
      • 2011-06-26
      • 2020-10-01
      • 2018-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-17
      • 1970-01-01
      • 2011-08-09
      相关资源
      最近更新 更多