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


当前回答

下面是我的方法(围绕rand()的包装器):

我还扩展到允许min为INT_MIN而max为INT_MAX的情况,这通常不可能单独使用rand(),因为它返回从0到RAND_MAX的值,包括(1/2范围)。

像这样使用它:

const int MIN = 1;
const int MAX = 1024;
// Get a pseudo-random number between MIN and MAX, **inclusive**.
// Seeding of the pseudo-random number generator automatically occurs
// the very first time you call it.
int random_num = utils_rand(MIN, MAX);

定义和氧描述:

#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>

/// \brief      Use linear interpolation to rescale, or "map" value `val` from range
///             `in_min` to `in_max`, inclusive, to range `out_min` to `out_max`, inclusive.
/// \details    Similar to Arduino's ingenious `map()` function:
///             https://www.arduino.cc/reference/en/language/functions/math/map/
///
/// TODO(gabriel): turn this into a gcc statement expression instead to prevent the potential for
/// the "double evaluation" bug. See `MIN()` and `MAX()` above.
#define UTILS_MAP(val, in_min, in_max, out_min, out_max) \
    (((val) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min))

/// \brief      Obtain a pseudo-random integer value between `min` and `max`, **inclusive**.
/// \details    1. If `(max - min + 1) > RAND_MAX`, then the range of values returned will be
///             **scaled** to the range `max - min + 1`, and centered over the center of the
///             range at `(min + max)/2`. Scaling the numbers means that in the case of scaling,
///             not all numbers can even be reached. However, you will still be assured to have
///             a random distribution of numbers across the full range.
///             2. Also, the first time per program run that you call this function, it will
///             automatically seed the pseudo-random number generator with your system's
///             current time in seconds.
/// \param[in]  min         The minimum pseudo-random number you'd like, inclusive. Can be positive
///                         OR negative.
/// \param[in]  max         The maximum pseudo-random number you'd like, inclusive. Can be positive
///                         OR negative.
/// \return     A pseudo-random integer value between `min` and `max`, **inclusive**.
int utils_rand(int min, int max)
{
    static bool first_run = true;
    if (first_run)
    {
        // seed the pseudo-random number generator with the seconds time the very first run
        time_t time_now_sec = time(NULL);
        srand(time_now_sec);
        first_run = false;
    }

    int range = max - min + 1;
    int random_num = rand();  // random num from 0 to RAND_MAX, inclusive

    if (range > RAND_MAX)
    {
        static_assert(
            sizeof(long int) > sizeof(int),
            "This must be true or else the below mapping/scaling may have undefined overflow "
            "and not work properly. In such a case, try casting to `long long int` instead of "
            "just `long int`, and update this static_assert accordingly.");

        random_num = UTILS_MAP((long int)random_num, (long int)0, (long int)RAND_MAX, (long int)min,
                               (long int)max);
        return random_num;
    }

    // This is presumably a faster approach than the map/scaling function above, so do this faster
    // approach below whenever you don't **have** to do the more-complicated approach above.
    random_num %= range;
    random_num += min;

    return random_num;
}

参见:

[我在写下上面的答案后发现了这个问答,但它显然非常相关,他们对非缩放范围的情况做了同样的事情]我如何从rand()中获得特定的数字范围? [我需要进一步研究和阅读这个答案-似乎有一些好的观点,保持良好的随机性不使用模量]我如何从rand()得到一个特定的数字范围? http://c-faq.com/lib/randrange.html

其他回答

与此相关的特定于glibc的函数(应该在大多数Linux环境中都可以找到)是random(),或者您可能对其线程安全版本random_r()感兴趣。在将结构体random_data传递给random_r()之前,必须使用initstate_r()初始化它。

下面是一个快速的代码示例:

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

void xxx (void) {
    unsigned int seed = (unsigned int) time(NULL);
    char rnd_state[17] = {0};
    struct random_data rnd_st_buf = {0};
    initstate_r(seed, &rnd_state[0], 17, &rnd_st_buf);
    for(size_t idx = 0; idx < 8; idx++) {
        int32_t rnd_int = 0;
        char rnd_seq_str[6] = {0};
        random_r(&rnd_st_buf, &rnd_int);
        memcpy((char *)&rnd_seq_str[0], (char *)&rnd_int, 4);
        printf("random number : 0x%08x,  \n", rnd_int);
    }
}

如果你的系统支持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)

Arc4random返回一个随机的32位无符号整数。

Arc4random_buf将随机内容放在参数buf: void *中。内容的数量由bytes: size_t参数决定。

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

Arc4random_stir从/dev/urandom读取数据,并将数据传递给arc4random_adrandom以额外随机化它的内部随机数池。

arc4random_adrandom由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 sizeof(long) bytes (usually 8) into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}

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

urandom函数基本上与rand调用相同,只是更安全,并且它返回一个长(容易更改)。

但是,/dev/urandom可能会有点慢,所以建议您使用它作为不同随机数生成器的种子。

如果您的系统没有/dev/urandom,但是有一个/dev/random或类似的文件,那么您可以简单地将传递的路径更改为在urandom_init中打开。urandom_init和urandom中使用的调用和api(我相信)是POSIX兼容的,因此,即使不是所有POSIX兼容的系统,也应该在大多数系统上工作。

注意:如果可用熵不足,从/dev/urandom读取将不会阻塞,因此在这种情况下生成的值可能是密码不安全的。如果您担心这一点,那么使用/dev/random,如果熵不足,它总是会阻塞。

如果您在另一个系统(即。Windows),然后使用rand或一些内部Windows特定平台依赖的不可移植API。

urandom, rand或arc4random调用的包装器函数:

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

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

有人很好地解释了为什么使用rand()在给定范围内生成均匀分布的随机数是一个坏主意,我决定看看输出到底有多倾斜。我的测试案例是公平掷骰子。下面是C代码:

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

int main(int argc, char *argv[])
{
    int i;
    int dice[6];

    for (i = 0; i < 6; i++) 
      dice[i] = 0;
    srand(time(NULL));

    const int TOTAL = 10000000;
    for (i = 0; i < TOTAL; i++)
      dice[(rand() % 6)] += 1;

    double pers = 0.0, tpers = 0.0;
    for (i = 0; i < 6; i++) {
      pers = (dice[i] * 100.0) / TOTAL;
      printf("\t%1d  %5.2f%%\n", dice[i], pers);
      tpers += pers;
    }
    printf("\ttotal:  %6.2f%%\n", tpers);
}

这是它的输出:

 $ gcc -o t3 t3.c
 $ ./t3 
        1666598  16.67%     
        1668630  16.69%
        1667682  16.68%
        1666049  16.66%
        1665948  16.66%
        1665093  16.65%
        total:  100.00%
 $ ./t3     
        1667634  16.68%
        1665914  16.66%
        1665542  16.66%
        1667828  16.68%
        1663649  16.64%
        1669433  16.69%
        total:  100.00%

我不知道你需要你的随机数有多统一,但上面的看起来足够统一,满足大多数需求。

编辑:用比time(NULL)更好的东西初始化PRNG是个好主意。

尽管这里所有人都建议使用rand(),但除非迫不得已,否则您不会想要使用rand() !rand()生成的随机数通常非常糟糕。引用Linux手册页:

Linux C库中的rand()和srand()版本使用与random(3)和srandom(3)相同的随机数生成器,因此低阶位应该与高阶位一样随机。但是,在旧的rand()实现上,以及在不同系统上的当前实现上,低阶位比高阶位随机得多。当需要良好的随机性时,不要在旨在可移植的应用程序中使用此函数。(请使用random(3)。)

关于可移植性,random()也由POSIX标准定义了很长一段时间。rand()更老,它已经出现在第一个POSIX.1规范(IEEE Std 1003.1-1988)中,而random()首次出现在POSIX.1-2001 (IEEE Std 1003.1-2001)中,然而当前的POSIX标准已经是POSIX.1-2008 (IEEE Std 1003.1-2008),仅在一年前收到了更新(IEEE Std 1003.1-2008, 2016版)。所以我认为random()是非常可移植的。

POSIX.1-2001还引入了lrand48()和mrand48()函数,参见这里:

此函数族应使用线性同余算法和48位整数算术生成伪随机数。

一个很好的伪随机源是arc4random()函数,它在许多系统上都可用。不是任何官方标准的一部分,在1997年左右出现在BSD中,但你可以在Linux和macOS/iOS等系统上找到它。

你可以生成随机字符,然后将它们视为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 ;
}