首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在rand()中使用位移位以允许更大的随机范围

在rand()中使用位移位以允许更大的随机范围
EN

Stack Overflow用户
提问于 2021-04-27 00:55:10
回答 3查看 330关注 0票数 1

我正在检查一个函数以生成基映射的键,并发现rand()的实现对我来说很新奇。

以下是功能:

代码语言:javascript
复制
static int make_random(RadixMap *map)
{
    size_t i = 0;

    for (i = 0; i < map->max - 1; i++){
        uint32_t key = (uint32_t) (rand() | (rand() << 16));<--This was interesting
        check(RadixMap_add(map, key, i) == 0, "Failed to add key %u", key);
    }

    return i;

error:
    return 0;
}

----- Type definitions --------
typedef union RMElement {
    uint64_t raw;
    struct {
        uint32_t key;
        uint32_t value;
    } data;
} RMElement; 

typedef struct RadixMap {
    size_t max; 
    size_t end;
    uint32_t counter;
    RMElement *contents;
    RMElement *temp; 
} RadixMap;

从ex35学习C-- Zed的艰难之路

我发现最有趣的部分是

代码语言:javascript
复制
uint32_t key = (uint32_t) (rand() | (rand() << 16)); <-- This was interesting

这对我来说很有趣,因为这样做是可能的。

代码语言:javascript
复制
uint32_t key = rand();

由于RAND_MAX (0x7FFFFFFF)小于uint32_t MAX (0xFFFFFF)

位移位实现看起来有以下优点。

  1. 允许较大的随机值范围,0xFFFFFFF与0x7FFFFFF
  2. 值(初始0除外)至少是5位小数(65537) (0x10001)
  3. 减少看到"0“的概率。

和下面的缺点

  1. 增加了代码复杂度?

使用这个rand()的位移位实现还有其他原因吗?

我一直试图找出在代码评审中使用此实现的原因,并希望确保我的思路正确。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-04-27 01:39:29

C标准只保证RAND_MAX至少为32767。这段代码通过两次调用rand并移动以确保它至少具有30位的随机性来解释这一点。

然而,这并不能恰当地解释RAND_MAX更大的情况。

rand函数返回一个已签名的int。如果RAND_MAXINT_MAX相同,rand() << 16很可能将"1“位转换为符号位,从而触发未定义行为

为处理这两种情况,实现这一目标的适当方法是:

代码语言:javascript
复制
uint32_t key = rand() | ((uint32_t)rand() << 16));

因为左移位,所以只要移位量小于类型的大小,就可以很好地定义一个无符号数字。

或者更好的是:

代码语言:javascript
复制
uint32_t key = (((uint32_t)rand() & 0x7FFF) << 17) | 
               (((uint32_t)rand() & 0x7FFF) << 2) | 
               ((uint32_t)rand() & 0x3);

才能得到完整的32位随机性。

票数 2
EN

Stack Overflow用户

发布于 2021-04-27 01:44:59

uint32_t key = (uint32_t) (rand() | (rand() << 16));也有缺点。

  • RAND_MAX != 65535不均匀时,这是通常的情况。
  • int为16位时,未定义的行为。另外,由于有符号整数溢出的可能性,在其他情况下也使用rand() << 16
  • 演员阵容太晚了,无法抵御狭窄的int。实际上,与uint32_t key = rand() | (rand() << 16); uint32_t key = rand() + (rand() * (RAND_MAX+(uint32_t key)1);一样,更有意义。

一个关键的缺点是使用|来追加右边的零位与RAND_MAX的位宽不一样。

第二个弱点是假设转移比乘以2的力量要好。无论哪种方式,一个好的编译器都能发出高效的代码。

相反,根据需要根据RAND_MAX调用随机函数(1次、2次或3次)。当RAND_MAX是一个Mersenne数时,下面的效果很好。

有没有方法在编译时计算整数类型的宽度?

代码语言:javascript
复制
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))
// Bit width of RAND_MAX, which is at least 15
#define RAND_MAX_BITS IMAX_BITS(RAND_MAX)

_Static_assert(((RAND_MAX + 1u) & RAND_MAX) == 0, "RAND_MAX is not a Mersenne number");

uint32_t rand32(void) {
  uint32_t r = rand();
  #if RAND_MAX_BITS < 32
    r = (r << RAND_MAX_BITS) | rand();
  #endif
  #if RAND_MAX_BITS*2 < 32
    r = (r << RAND_MAX_BITS) | rand();
  #endif
  return r;
}

(位移位)增加了代码复杂度?

不是的。

使用这个rand()的位移位实现还有其他原因吗?

OP的代码并不是统一的,因为它通常倾向于一个比特,因为它的潜力或者十五号以后的位数。

我一直在努力找出使用这个实现的原因.

不要用它。

票数 2
EN

Stack Overflow用户

发布于 2021-04-27 04:35:09

或者,你可以用一个非常快的随机数发生器。注意,没有看到没有太多零字节的值。

代码语言:javascript
复制
uint64_t
xorshift128plus(uint64_t seed[2])
{
    uint64_t x = seed[0];
    uint64_t y = seed[1];
    seed[0] = y;
    x ^= x << 23;
    seed[1] = x ^ y ^ (x >> 17) ^ (y >> 26);
    return s[1] + y;
}

将结果转换为浮动或调制最大int值.

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67275561

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档