有没有一个函数生成一个随机int数在C?或者我必须使用第三方库?


当前回答

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

正如在如何在各种编程语言中安全地生成随机数中提到的,你会想要做以下事情之一:

使用libsodium的randombytes API 重新实现你自己需要的libsodium的sysrandom实现,非常小心 更广泛地说,使用/dev/urandom,而不是/dev/random。而不是OpenSSL(或其他用户空间prng)。

例如:

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

Randombytes_uniform()是加密安全且无偏倚的。

其他回答

<stdlib.h>中的rand()函数返回一个介于0和RAND_MAX之间的伪随机整数。你可以使用srand(unsigned int seed)来设置种子。

通常的做法是将%操作符与rand()结合使用以获得不同的范围(但请记住,这在一定程度上破坏了一致性)。例如:

/* random int between 0 and 19 */
int r = rand() % 20;

如果你真的在乎一致性,你可以这样做:

/* 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;
  }
}

这是一个在你选择的两个数字之间得到一个随机数的好方法。

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

第一次输出:39

第二次输出:61

第三次输出:65

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

你可以生成随机字符,然后将它们视为int:

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

typedef double rand_type; // change double to int

rand_type my_rand() {
    char buff[sizeof(rand_type)];
    for (size_t i = 0 ; i < sizeof(rand_type) ; ++i)
        buff[i] = (char) rand();
    return *(rand_type *) buff;
}

int main() {
    int i ; // srand as you want
    for (i = 0 ; i < 10 ; ++i)
        printf("%g\n", my_rand()); // change %g to %d
    return 0 ;
}

对于c, STL不存在,你必须调用rand,或者更好的是,随机。它们在标准库头文件stdlib.h中声明。rand是POSIX, random是BSD规范函数。

rand和random之间的区别是random返回一个更有用的32位随机数,而rand通常返回一个16位数。BSD手册显示rand的较低位是循环的和可预测的,因此rand对于较小的数字可能是无用的。

标准的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;
}

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

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

对于浮点数,我们需要53个随机位,因为double类型拥有53位精度(假设它是IEEE double类型)。如果我们使用超过53位,就会产生舍入偏差。有些程序员写rand() / (double)RAND_MAX这样的代码,但是rand()可能只返回31位,或者在Windows中只返回15位。

OpenSSL的RAND_bytes()可能通过读取Linux中的/dev/urandom来自行播种。如果我们需要很多随机数,从/dev/urandom读取它们会很慢,因为它们必须从内核复制。允许OpenSSL从种子中生成更多的随机数会更快。

更多关于随机数的内容:

Perl的Perl_seed()是一个如何在C中为srand()计算种子的例子。如果它不能读取/dev/ urrandom,它会混合来自当前时间、进程ID和一些指针的比特。 OpenBSD的arc4random_uniform()解释了模偏置。 random的Java API描述了从随机整数中去除偏差的算法,并将53位打包到随机浮点数中。