赞
踩
是否有在C中生成随机int数的函数? 或者我必须使用第三方库吗?
让我们来看看。 首先,我们使用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;
- }
试试这个,我把它从上面提到的一些概念中加入:
- /*
- 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;
- }
rand()
是生成随机数的最方便的方法。
您也可以通过random.org等在线服务获取随机数。
- #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>
- #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
之后的值更改为您选择的任何数字,并在这两个数字之间为您生成一个随机数。
听到一个很好的解释为什么使用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是个好主意。
- #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]);
- }
标准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值均匀地映射到10000个值从0到9999。
对于浮点数,我们需要53个随机位,因为double
保持53位精度(假设它是IEEE的两倍)。 如果我们使用超过53位,我们会得到舍入偏差。 一些程序员编写像rand() / (double)RAND_MAX
这样的代码,但rand()
可能只返回31位,或者只返回Windows中的15位。
OpenSSL的RAND_bytes()
可以通过在Linux中读取/dev/urandom
来实现种子本身。 如果我们需要很多随机数,那么从/dev/urandom
读取它们的速度太慢,因为它们必须从内核中复制。 允许OpenSSL从种子生成更多随机数更快。
更多关于随机数字:
srand()
计算种子的示例。 它混合来自当前时间,进程ID和一些指针的位,如果它不能读取/dev/urandom
。 如果您的系统支持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_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;
- }
urandom_init
函数打开/dev/urandom
设备,并将文件描述符放在urandom_fd
。
urandom
函数基本上与对rand
的调用相同,除了更安全,它返回一个long
(容易更改)。
但是, /dev/urandom
可能有点慢,因此建议您将其用作不同随机数生成器的种子。
如果您的系统没有/dev/urandom
,但确实有/dev/random
或类似的文件,那么您只需在urandom_init
更改传递给open
的路径。 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;
- }
希望比使用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());
- }
我的简约解决方案适用于范围[min, max)
随机数。 在调用函数之前使用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));
- }
如何在各种编程语言中安全地生成随机数 ,您需要执行以下操作之一:
randombytes
API /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()
是加密安全且无偏的。
我在最近的应用程序中遇到了一个严重的伪随机数生成器问题:我通过pyhton脚本重复调用我的C程序,我使用以下代码作为种子:
srand(time(NULL))
但是,因为:
man srand
); time
将每次返回相同的值。 我的程序生成了相同的数字序列。 你可以做3件事来解决这个问题:
混合时间输出与运行时更改的其他信息(在我的应用程序中,输出名称):
srand(time(NULL) | getHashOfString(outputName))
我使用djb2作为我的哈希函数。
增加时间分辨率。 在我的平台上, clock_gettime
可用,所以我使用它:
#include<time.h> struct timespec nanos; clock_gettime(CLOCK_MONOTONIC, &nanos) srand(nanos.tv_nsec);
一起使用两种方法:
#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和upperLimit-1之间生成一个随机数
即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
尽管所有人都在这里建议rand()
,你不想使用rand()
除非你必须! rand()
产生的随机数通常非常糟糕。 引用Linux手册页:
Linux C库中的
rand()
和srand()
版本使用与randomrandom(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等系统上找到它。
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
-
- //generate number in range [min,max)
- int random(int min, int max){
- int number = min + rand() % (max - min);
- return number;
- }
-
- //Driver code
- int main(){
- srand(time(NULL));
- for(int i = 1; i <= 10; i++){
- printf("%d\t", random(10, 100));
- }
- return 0;
- }
对于C,STL不存在。你必须random
调用rand
,或者更好。 这些在标准库头文件stdlib.h
中声明。 rand
是POSIX, random
是BSD规范函数。
rand
和random
之间的区别在于random
返回一个更可用的32位随机数, rand
通常返回一个16位数。 BSD联机帮助页显示rand
的低位是循环且可预测的,因此rand
对于小数字可能是无用的。
好吧,STL是C ++,而不是C,所以我不知道你想要什么。 但是,如果你想要C,则有rand()
和srand()
函数:
- int rand(void);
-
- void srand(unsigned seed);
这些都是ANSI C的一部分。还有random()
函数:
long random(void);
但据我所知, random()
不是标准ANSI C.第三方库可能不是一个坏主意,但这完全取决于你真正需要生成的数字的随机性。
你想使用rand()
。 注意( 非常重要 ):确保为rand函数设置种子。 如果你不这样做,你的随机数不是真正随机的 。 这非常非常非常重要。 值得庆幸的是,您通常可以使用系统滴答计时器和日期的某种组合来获得良好的种子。
<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;
- }
- }
FWIW,答案是肯定的,有一个名为rand
的stdlib.h
函数; 此功能主要针对速度和分布进行调整,而不是针对不可预测性。 几乎所有用于各种语言和框架的内置随机函数都默认使用此函数。 还有“加密”随机数生成器,它们的可预测性要低得多,但运行速度要慢得多。 这些应该用于任何类型的安全相关的应用程序。
看看ISAAC (间接,移位,累积,添加和计数)。 它均匀分布,平均周期长度为2 ^ 8295。
注意 :不要使用
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.
编辑 :在Linux上,您可能更喜欢使用random和srandom 。
如果您需要比stdlib
提供的质量更好的伪随机数,请查看Mersenne Twister 。 它也更快。 示例实现很多,例如这里 。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。