【问题标题】:How to generate a random number from whole range of int in C?如何从C中的整个int范围生成随机数?
【发布时间】:2016-02-18 19:02:14
【问题描述】:
unsigned const number = minimum + (rand() % (maximum - minimum + 1))

我知道如何(轻松地)生成一个范围内的随机数,例如从 0 到 100。但是来自int全范围 的随机数呢(假设 sizeof(int) == 4 ),即从INT_MININT_MAX,两者都包含?

对于密码学等,我不需要这个,但大致均匀的分布会很好,我需要 很多 这些数字。

我目前使用的方法是在 0 到 255(含)范围内生成 4 个随机数,并进行一些杂乱的转换和位操作。我想知道是否有更好的方法。

【问题讨论】:

  • 不,但显然我的 google foo 缺少?如果这是直截了当的,那么请回答。
  • @DanielJour 他一整天都这样。
  • 你做得很好:只注意负数。您不想对负数进行二元运算(或、异或和)。
  • @Olaf Pseudo random 对我来说没问题。
  • 亲爱的反对者:请解释为什么你反对我。如果已经有关于 SO 的类似问题,请链接到它。 (我搜索了很多)

标签: c random


【解决方案1】:

在我的系统上,RAND_MAX 是 32767,即 15 位。因此,对于 32 位 unsigned 只需调用 3 次并移位或掩码等。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
    unsigned rando, i;
    srand((unsigned)time(NULL));
    for (i = 0; i < 3; i++) {
        rando = ((unsigned)rand() << 17) | ((unsigned)rand() << 2) | ((unsigned)rand() & 3);
        printf("%u\n", rando);
    }
    return 0;
}

程序输出:

3294784390
3748022412
4088204778

【讨论】:

  • 好的,我正在做类似的 atm,但这更好,因为 IIRC RAND_MAX 保证至少有 15 位。 (我只用了其中的 8 个)
  • 这不是便携式的。
  • RAND_MAX 不是32767 时,((unsigned)rand() &lt;&lt; 17) | ((unsigned)rand() &lt;&lt; 2) | ... 是非常糟糕的随机数代码。此代码应针对 RAND_MAXassert/_Static_assert 条件进行调整。
  • @chux 最好使用^ 而不是|
【解决方案2】:

作为参考,我添加了我一直在使用的内容:

int random_int(void) {
  assert(sizeof(unsigned int) == sizeof(int));
  unsigned int accum = 0;
  size_t i = 0;
  for (; i < sizeof(int); ++i) {
    i <<= 8;
    i |= rand() & 0x100;
  }
  // Attention: Implementation defined!
  return (int) accum;
}

但我更喜欢 Weather Vane 的解决方案,因为它使用较少的 rand() 调用,因此更多地利用了它生成的(希望是好的)分布。

【讨论】:

  • 使用UINT_MAX, INT_MAX, INT_MIN 比使用assert(sizeof(unsigned int)... 更强的资格测试。它也可以用于宏预处理,以便在运行时不编译而不是失败。仍然是一个很好的基本方法。 (应该在问题中有此代码。)
【解决方案3】:

无论rand() 的范围是什么,或者我们正在寻找什么大小的结果,我们都应该能够通过积累足够的位来填充给定的类型:

// can be any unsigned type.
typedef uint32_t uint_type;
#define RAND_UINT_MAX ((uint_type) -1)

uint_type rand_uint(void)
{
    // these are all constant and factor is likely a power of two.
    // therefore, the compiler has enough information to unroll
    // the loop and can use an immediate form shl in-place of mul.
    uint_type factor = (uint_type) RAND_MAX + 1;
    uint_type factor_to_k = 1;
    uint_type cutoff = factor ? RAND_UINT_MAX / factor : 0;
    uint_type result = 0;

    while ( 1 ) {
        result += rand() * factor_to_k;
        if (factor_to_k <= cutoff)
            factor_to_k *= factor;
        else
            return result;
    }
}

注意:调用rand() 的最少次数是填充所有位所必需的。

让我们verify 这给出了一个均匀分布。

此时我们可以将rand_uint() 的结果转换为类型int 并完成,但在指定范围内获取输出更有用。问题是:当操作数是 int 类型时,我们如何到达INT_MAX

嗯...我们不能。我们需要使用范围更大的类型:

int uniform_int_distribution(int min, int max)
{
    // [0,1) -> [min,max]
    double canonical = rand_uint() / (RAND_UINT_MAX + 1.0);
    return floor(canonical * (1.0 + max - min) + min);
}

作为最后一点,可能值得以 double 类型来实现随机函数,即为DBL_MANT_DIG 积累足够的位并返回 范围内的结果[0,1)。事实上,std::generate_canonical 就是这样做的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-07
    • 2023-03-04
    • 2013-11-19
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    相关资源
    最近更新 更多