如何在C中生成随机int?

Kre*_*dns 527 c random

是否有在C中生成随机int数的函数?或者我必须使用第三方库吗?

Łuk*_*Lew 642

注意:请勿rand()用于安全性.如果您需要加密安全号码,请参阅此答案.

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

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.
Run Code Online (Sandbox Code Playgroud)

编辑:在Linux上,您可能更喜欢使用random和srandom.

  • 为简单起见+1,但强调srand()只应调用*一次*可能是一个好主意.此外,在线程应用程序中,您可能希望确保每个线程存储生成器的状态,并为每个线程为生成器播种一次. (190认同)
  • @trusktr,它很复杂.这是一个原因:`time()`每秒只改变一次.如果从`time()`开始播种,对于每次调用`rand()`,那么你将在一秒钟内为每个调用获得相同的值.但更大的原因是`rand()`和类似函数的属性最适用于每次运行只播种一次的用例,而不是每次调用.取决于具有未经测试或未经证实的属性的"随机性"会导致麻烦. (41认同)
  • 避免使用强制转换的编译器警告:`srand((unsigned int)time(NULL));` (21认同)
  • @trusktr是一个简单的线性同余生成器(通常是`rand()`)用`rand()`播种最多只会产生影响,最坏的情况会破坏生成器的已知特性.这是一个深刻的主题.首先阅读[Knuth Vol 2](http://amzn.com/0321751043)关于随机数的第3章,作为对数学和陷阱的最佳介绍. (9认同)
  • 请记住,这仍然是看到PRNG的弱点.就在去年,Linux上的一个cryptolocker型病毒犯了时间播种的错误,这**大大减少了搜索空间.你所要做的就是了解感染发生的时间,然后尝试从那个时候开始种子.最后我听说,随机性的最佳来源是/ dev/urandom,据推测,它来自混乱的混合源,如硬件上的温度.但是,如果你真正想要的是你的程序在每次运行时采取不同的行动,那么上述解决方案就没问题了. (9认同)
  • @ user2600219`time(NULL)`返回指针的当前时间,如果它不同于NULL.你可以在那里使用一些变量,但它不会影响`time()`的结果,传递的变量将被当前时间覆盖. (3认同)
  • @RBerteig为什么只能调用一次? (2认同)
  • 为什么你使用NULL?你能输入别的吗? (2认同)
  • 由于时间显然不是产生种子价值的好方法,那么更好的方法是什么呢? (2认同)
  • (int)&amp;main 在运行之间不一定有所不同 (2认同)

Lau*_*ves 231

rand()函数<stdlib.h>返回0之间的伪随机整数RAND_MAX.您可以使用srand(unsigned int seed)设置种子.

通常的做法%是结合使用操作符rand()来获得不同的范围(但请记住,这会在某种程度上抛弃均匀性).例如:

/* random int between 0 and 19 */
int r = rand() % 20;
Run Code Online (Sandbox Code Playgroud)

如果你真的关心一致性,你可以这样做:

/* Returns an integer in the range [0, n).
 *
 * Uses rand(), and so is affected-by/affects the same seed.
 */
int randint(int n) {
  if ((n - 1) == RAND_MAX) {
    return rand();
  } else {
    // Supporting larger values for n would requires an even more
    // elaborate implementation that combines multiple calls to rand()
    assert (n <= RAND_MAX)

    // Chop off all of the values that would cause skew...
    int end = RAND_MAX / n; // truncate skew
    assert (end > 0);
    end *= n;

    // ... and ignore results from rand() that fall above that limit.
    // (Worst case the loop condition should succeed 50% of the time,
    // so we can expect to bail out of this loop pretty quickly.)
    int r;
    while ((r = rand()) >= end);

    return r % n;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @Lazer:这就是为什么我说"虽然请记住,这会在一定程度上抛弃均匀性". (33认同)
  • 这是一种常见的做法*好吧,但不是正确的做法.见[this](http://stackoverflow.com/questions/2999075/generate-a-random-number-within-range/2999130#2999130)和[this](http://stackoverflow.com/questions/288739/产生随机号码均匀-过整个范围/ 288869#288869). (17认同)
  • @AbhimanyuAryan`%`是模数运算符.它给出了整数除法的余数,因此`x%n`总是会给出一个介于'0'和'n - 1'之间的数字(只要`x`和`n`都是正数).如果你仍然觉得这很混乱,那么试着编写一个"i"从0到100计数的程序,然后打印出`i%n`来表示你选择的某个`n`小于100. (3认同)
  • @necromancer我继续前进并添加了一个完美统一的解决方案. (2认同)
  • @Lazer你发布的第二个链接实际上仍然不完全统一.铸造成双人和背部并没有帮助.你发布的第一个链接有一个完全统一的解决方案,虽然它会为小上限循环一个*lot*.我为这个答案添加了一个完全统一的解决方案,即使对于小上限也不应该循环. (2认同)

Sco*_*ski 52

如果您需要安全的随机字符或整数:

正如在讨论如何安全地生成各种编程语言的随机数,你会想要做下列之一:

例如:

#include "sodium.h"

int foo()
{
    char myString[32];
    uint32_t myInt;

    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized, it is not safe to use */
        return 1; 
    }


    /* myString will be an array of 32 random bytes, not null-terminated */        
    randombytes_buf(myString, 32);

    /* myInt will be a random number between 0 and 9 */
    myInt = randombytes_uniform(10);
}
Run Code Online (Sandbox Code Playgroud)

randombytes_uniform() 密码安全且无偏见.

  • 为什么不鼓励使用 OpenSSL 和其他用户态 PRNG?OpenSSL 的“RAND_bytes()”文档称它是一个加密安全的 PRNG。 (2认同)

小智 29

让我们来看看.首先,我们使用srand()函数为随机化器播种.基本上,计算机可以根据提供给srand()的数字生成随机数.如果给出相同的种子值,则每次都会生成相同的随机数.

因此,我们必须使用始终在变化的值为随机化器播种.我们通过使用time()函数向其提供当前时间的值来实现此目的.

现在,当我们调用rand()时,每次都会产生一个新的随机数.

#include <stdio.h>

int random_number(int min_num, int max_num);

int main(void)
{
    printf("Min : 1 Max : 40 %d\n", random_number(1,40));
    printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
    return 0;
}

int random_number(int min_num, int max_num)
{
    int result = 0, low_num = 0, hi_num = 0;

    if (min_num < max_num)
    {
        low_num = min_num;
        hi_num = max_num + 1; // include max_num in output
    } else {
        low_num = max_num + 1; // include max_num in output
        hi_num = min_num;
    }

    srand(time(NULL));
    result = (rand() % (hi_num - low_num)) + low_num;
    return result;
}
Run Code Online (Sandbox Code Playgroud)

  • 好的代码,但不是一个好主意调用'srand(time(NULL));'.当在for循环中调用时,此方法产生相同的数字. (12认同)

MH1*_*114 25

如果您需要比stdlib提供的质量更好的伪随机数,请查看Mersenne Twister.它也更快.示例实现很多,例如这里.

  • 不要使用Mersenne Twister,使用像xoroshiro128 +或PCG这样的好东西.[(相关链接.)](http://cs.stackexchange.com/questions/50059/why-is-the-mersenne-twister-regarded-as-good) (4认同)
  • +1:看起来很酷,但我只是在做一个猜谜游戏.如果我打算在业务应用程序中使用随机数生成器,那么我肯定会使用它. (2认同)

Geo*_*ler 17

标准的C函数是rand().这对于单人纸牌来说已经足够好了,但这太可怕了.许多实现rand()循环通过一个简短的数字列表,而低位循环更短.一些程序调用的rand()方式很糟糕,计算一个好的种子传递给srand()很难.

在C中生成随机数的最佳方法是使用OpenSSL之类的第三方库.例如,

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } u;

    do {
        if (!RAND_bytes(u.c, sizeof(u.c))) {
            fprintf(stderr, "Can't get random bytes!\n");
            exit(1);
        }
    } while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
    return u.i % limit;
}

/* Random double in [0.0, 1.0) */
double random_double() {
    union {
        uint64_t i;
        unsigned char c[sizeof(uint64_t)];
    } u;

    if (!RAND_bytes(u.c, sizeof(u.c))) {
        fprintf(stderr, "Can't get random bytes!\n");
        exit(1);
    }
    /* 53 bits / 2**53 */
    return (u.i >> 11) * (1.0/9007199254740992.0);
}

int main() {
    printf("Dice: %d\n", (int)(random_uint(6) + 1));
    printf("Double: %f\n", random_double());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

为什么这么多代码?Java和Ruby等其他语言具有随机整数或浮点数的函数.OpenSSL只提供随机字节,因此我尝试模仿Java或Ruby如何将它们转换为整数或浮点数.

对于整数,我们希望避免模偏差.假设我们从中获得了一些随机的4位整数rand() % 10000,但rand()只能返回0到32767(就像在Microsoft Windows中一样).从27到9999中,每个数字从0到2767的出现频率会更高.为了消除偏差,我们可以rand()在值低于2768时重试,因为从2768到32767的30000值均匀映射到从0到0的10000个值9999.

对于浮点数,我们需要53个随机位,因为a double保持53位精度(假设它是IEEE的两倍).如果我们使用超过53位,我们会得到舍入偏差.有些程序员编写类似的代码rand() / (double)RAND_MAX,但rand()在Windows中可能只返回31位或15位.

OpenSSL的RAND_bytes()种子本身,也许是通过阅读/dev/urandomLinux.如果我们需要许多随机数,那么从它们全部读取它们会太慢/dev/urandom,因为它们必须从内核中复制.允许OpenSSL从种子生成更多随机数更快.

更多关于随机数字:


Éle*_*tra 10

如果您的系统支持arc4random一系列功能,我建议使用这些rand功能代替标准功能.

这个arc4random家庭包括:

uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)
Run Code Online (Sandbox Code Playgroud)

arc4random 返回一个随机的32位无符号整数.

arc4random_buf将随机内容放入其参数中buf : void *.内容量由bytes : size_t参数确定.

arc4random_uniform返回一个随机的32位无符号整数,它遵循以下规则:0 <= arc4random_uniform(limit) < limit其中limit也是无符号的32位整数.

arc4random_stir从数据中读取数据/dev/urandom并将数据传递arc4random_addrandom到另外随机化它的内部随机数池.

arc4random_addrandom用于arc4random_stir根据传递给它的数据填充它的内部随机数池.

如果您没有这些功能,但是您使用的是Unix,则可以使用以下代码:

/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */

int urandom_fd = -2;

void urandom_init() {
  urandom_fd = open("/dev/urandom", O_RDONLY);

  if (urandom_fd == -1) {
    int errsv = urandom_fd;
    printf("Error opening [/dev/urandom]: %i\n", errsv);
    exit(1);
  }
}

unsigned long urandom() {
  unsigned long buf_impl;
  unsigned long *buf = &buf_impl;

  if (urandom_fd == -2) {
    urandom_init();
  }

  /* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}
Run Code Online (Sandbox Code Playgroud)

urandom_init函数打开/dev/urandom设备,并将文件描述符放入urandom_fd.

urandom函数与调用基本相同rand,除了更安全,它返回一个long(容易更改).

但是,/dev/urandom可能有点慢,因此建议您将其用作不同随机数生成器的种子.

如果您的系统没有/dev/urandom,但确实有一个/dev/random或类似的文件,那么你可以简单地改变传递到路径openurandom_init.该调用和API中使用的urandom_initurandom是(我相信)兼容POSIX,因此,应该在大多数工作,如果不是所有的POSIX兼容的系统.

注意:/dev/urandom如果可用的熵不足,则读取将不会阻止,因此在这种情况下生成的值可能是加密不安全的.如果您对此感到担心,那么请使用/dev/random,如果熵不足,将始终阻止.

如果您在另一个系统(即Windows)上,则使用rand或某些内部Windows特定于平台的非可移植API.

包装功能urandom,randarc4random来电:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}
Run Code Online (Sandbox Code Playgroud)


dre*_*lax 8

C不存在.你必须打电话rand,或者更好random.这些是在标准库头中声明的stdlib.h.rand是POSIX,random是BSD规范函数.

rand和之间的区别randomrandom返回一个更加可用的32位随机数,rand通常返回一个16位数.BSD联机帮助页显示较低位rand是循环且可预测的,因此rand对于小数字可能无用.

  • @Neil - 因为到目前为止所有的答案都提到了STL,我怀疑这个问题是快速编辑的,以删除anunecessary reference. (3认同)
  • 谁提到了STL? (2认同)

geo*_*tnz 7

看看ISAAC(间接,移位,累积,添加和计数).它均匀分布,平均周期长度为2 ^ 8295.

  • ISAAC是一个有趣的RNG,因为它的速度,但尚未收到严重的加密注意. (2认同)

小智 5

这是在您选择的两个数字之间获得随机数的好方法。

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

    #define randnum(min, max) \
        ((rand() % (int)(((max) + 1) - (min))) + (min))

int main()
{
    srand(time(NULL));

    printf("%d\n", randnum(1, 70));
}
Run Code Online (Sandbox Code Playgroud)

第一次输出:39

第二次输出:61

第三次输出:65

您可以将后面的值更改为randnum您选择的任何数字,它会在这两个数字之间为您生成一个随机数。


Kol*_*dar 5

我在最近的应用程序中遇到了伪随机数生成器的严重问题:我通过 Python 脚本反复调用我的 C 程序,并使用以下代码作为种子:

srand(time(NULL))
Run Code Online (Sandbox Code Playgroud)

然而,因为:

  • rand 将生成相同的伪随机序列,并在 srand 中提供相同的种子(请参阅man srand);
  • 如前所述,时间函数仅每秒发生变化:如果您的应用程序在同一秒内运行多次,则time每次都会返回相同的值。

我的程序生成了相同的数字序列。您可以做 3 件事来解决这个问题:

  1. 将时间输出与运行时更改的一些其他信息混合(在我的应用程序中,输出名称):

    srand(time(NULL) | getHashOfString(outputName))
    
    Run Code Online (Sandbox Code Playgroud)

    我使用djb2作为我的哈希函数。

  2. 提高时间分辨率。在我的平台上,clock_gettime可用,所以我使用它:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 一起使用这两种方法:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec | getHashOfString(outputName));
    
    Run Code Online (Sandbox Code Playgroud)

选项 3 可确保您(据我所知)获得最佳的种子随机性,但它可能仅在非常快的应用程序上产生差异。我认为选项 2 是一个安全的选择。