如何在C中生成一个随机数?

有一个函数可以在C中生成一个随机数吗? 或者我将不得不使用第三方库?

注意 :不要使用rand()来提高安全性。 如果您需要密码安全的号码, 请参阅此答案 。

 #include <time.h> #include <stdlib.h> srand(time(NULL)); // should only be called once int r = rand(); // returns a pseudo-random integer between 0 and RAND_MAX 

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

rand()运算符与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 { // Chop off all of the values that would cause skew... long end = RAND_MAX / n; // truncate skew assert (end > 0L); 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; } } 

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

就如何以各种编程语言安全地生成随机数而言 ,您将需要执行以下操作之一:

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

例如:

 #include "sodium.h" int foo() { char myString[32]; uint32_t myInt; /* 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提供的更好质量的伪随机数字,请查看Mersenne Twister 。 它也更快。 示例实现很丰富,例如这里 。

让我们通过这个。 首先我们使用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; } 

标准的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(uc, sizeof(uc))) { fprintf(stderr, "Can't get random bytes!\n"); exit(1); } } while (ui < (-limit % limit)); /* ui < (2**size % limit) */ return ui % 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(uc, sizeof(uc))) { fprintf(stderr, "Can't get random bytes!\n"); exit(1); } /* 53 bits / 2**53 */ return (ui >> 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值统一映射到10000从0到9999。

对于浮点数,我们需要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()是一个如何计算srand()中C的种子的例子。 如果不能读取/dev/urandom ,它会混合当前时间,进程ID和一些指针。
  • OpenBSD的arc4random_uniform()解释了模偏移。
  • java.util.Random的Java API描述了用于从随机整数中去除偏倚的algorithm,并将53位打包成随机的浮点数。

STL对于C不存在。您必须调用rand ,或者更好, random 。 这些是在标准库头stdlib.h中声明的。 rand是POSIX, random是BSD spec函数。

randrandom之间的区别是random返回一个更有用的32位随机数,而rand通常返回一个16位数。 BSD手册页显示rand的低位是循环的和可预测的,所以rand对于小数是没有用的。

如果你的系统支持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_addrandom以另外随机化它的内部随机数字池。

arc4random_addrandomarc4random_stir根据传递给它的数据填充它的内部随机数池。

如果你没有这些function,但你在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; } 

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

urandom函数基本上和rand的调用一样,除了更安全,它返回一个long (很容易改变)。

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

如果你的系统没有/dev/urandom ,但是有一个/dev/random或者类似的文件,那么你可以简单的改变在urandom_init open的path。 在urandom_initurandom中使用的调用和API是(我相信)符合POSIX,因此,即使不是所有POSIX兼容的系统,也应该可以工作。

注意:如果没有足够的可用熵,则从/dev/urandom读取将不会阻塞,因此在这种情况下生成的值可能是encryption不安全的。 如果你担心这个问题,那么使用/dev/random ,如果熵不够,它将一直阻塞。

如果你在另一个系统上(例如Windows),那么使用rand或者一些内部的Windows特定的平台相关的非可移植的API。

urandomrandarc4random调用的包装函数:

 #define RAND_IMPL /* urandom(see large code block) | rand | arc4random */ int myRandom(int bottom, int top){ return (RAND_IMPL() % (top - bottom)) + bottom; } 

看看ISAAC (Indirection,Shift,Accumulate,Add,和Count)。 其分布均匀,平均周期长度为2 ^ 8295。

你想使用rand() 。 注意( 非常重要 ):确保为rand函数设置种子。 如果你不这样做,你的随机数不是真正的随机数 。 这非常非常非常重要 谢天谢地,你通常可以使用一些系统滴答计时器和date的组合来获得一个好种子。

FWIW,答案是肯定的,有一个叫做randstdlib.h函数; 这个function主要是为了速度和分配而调整的,而不是不可预测的。 几乎所有内置的各种语言和框架的随机函数默认使用这个函数。 也有“密码”随机数发生器,可预测性更差,但运行速度慢得多。 这些应该用于任何types的安全相关的应用程序。

那么,STL是C ++,而不是C,所以我不知道你想要什么。 如果你想C,但是,有rand()srand()函数:

 int rand(void); void srand(unsigned seed); 

这些都是ANSI C的一部分。还有random()函数:

 long random(void); 

但据我所知, random()不是标准的ANSI C.第三方库可能不是一个坏主意,但这一切都取决于你真正需要产生的数字的随机性。

这是希望比只使用srand(time(NULL))更随机一点。

 #include <time.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL)); srand(rand()); for (int i = 0; i < 10; i++) printf("%d\n", rand()); } 

rand()是生成随机数最方便的方法。

你也可以从任何在线服务,如random.org抓住随机数。

这是一个很好的方法来获得您select的两个数字之间的随机数字。

 #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后的值更改为您select的任何数字,并在这两个数字之间为您生成一个随机数。

 #include <stdio.h> #include <dos.h> int random(int range); int main(void) { printf("%d", random(10)); return 0; } int random(int range) { struct time t; int r; gettime(&t); r = t.ti_sec % range; return r; } 
 #include <stdio.h> #include <stdlib.h> void main() { int visited[100]; int randValue, a, b, vindex = 0; randValue = (rand() % 100) + 1; while (vindex < 100) { for (b = 0; b < vindex; b++) { if (visited[b] == randValue) { randValue = (rand() % 100) + 1; b = 0; } } visited[vindex++] = randValue; } for (a = 0; a < 100; a++) printf("%d ", visited[a]); } 

听到一个很好的解释,为什么使用rand()在给定的范围内产生均匀分布的随机数是一个坏主意,我决定看看输出实际上是多么扭曲。 我的testing案例是掷骰子。 这是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。

在我最近的应用程序中,我使用伪随机数生成器存在一个严重的问题:我通过pyhton脚本重新调用了我的C程序,并将以下代码用作种子:

 srand(time(NULL)) 

但是,因为:

  • rand将产生相同的伪随机序列,在srand中给出相同的种子(参见man srand );
  • 如前所述,时间函数仅从第二个变为第二个:如果您的应用程序在同一秒内运行多次,则time将每次返回相同的值。

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

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

     srand(time(NULL) | getHashOfString(outputName)) 

    我用djb2作为我的散列函数。

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

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

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

选项3确保您(据我所知)最好的种子随机性,但它可能只在非常快的应用程序上产生差异。 在我看来,选项2是一个安全的赌注。

C程序生成9到50之间的随机数

 #include <time.h> #include <stdlib.h> int main() { srand(time(NULL)); int lowerLimit = 10, upperLimit = 50; int r = lowerLimit + rand() % (upperLimit - lowerLimit); printf("%d", r); } 

一般来说,我们可以在lowerLimit – 1和upperLimit之间生成一个随机数

即lowerLimit是包含的或者说r∈[lowerLimit,upperLimit)

在现代x86_64 CPU上,您可以通过_rdrand64_step()使用硬件随机数生成器

示例代码:

 #include <immintrin.h> uint64_t randVal; if(!_rdrand64_step(&randVal)) { // Report an error here: random number generation has failed! } // If no error occured, randVal contains a random 64-bit number 

我的简约解决scheme应该适用于范围[最小,最大)的随机数字。 之前使用srand(time(NULL))。

 int range_rand(int min_num, int max_num) { if(min_num >= max_num) { fprintf(stderr, "min_num is greater or equal than max_num!\n"); } return min_num + (rand() % (max_num - min_num)); } 

试试这个,我把上面提到的一些概念放在一起:

 /* Uses the srand() function to seed the random number generator based on time value, then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value. */ int random(int max) { srand((unsigned) time(NULL)); return (rand() % max) + 1; }