是否有在C中生成随机int数的函数?或者我必须使用第三方库吗?
答案 0 :(得分:623)
注意:请勿使用
rand()
来提高安全性。如果您需要加密安全号码,请改为see this answer。
#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 and srandom。
答案 1 :(得分:224)
rand()
中的<stdlib.h>
函数返回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;
}
}
答案 2 :(得分:46)
正如how to safely generate random numbers in various programming languages中所述,您需要执行以下操作之一:
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()
是加密安全且无偏见的。
答案 3 :(得分: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;
}
答案 4 :(得分:25)
如果您需要比stdlib
提供的质量更高的伪随机数,请查看Mersenne Twister。它也更快。示例实现很丰富,例如here。
答案 5 :(得分:16)
标准C函数是rand()
。它非常适合处理单人纸牌,但它很糟糕。许多rand()
的实现循环通过一个简短的数字列表,而低位的循环更短。某些程序调用{{1}}的方式很糟糕,计算好的种子传递给rand()
很难。
在C中生成随机数的最佳方法是使用OpenSSL之类的第三方库。例如,
srand()
为什么这么多代码? Java和Ruby等其他语言具有随机整数或浮点数的函数。 OpenSSL只提供随机字节,因此我尝试模仿Java或Ruby如何将它们转换为整数或浮点数。
对于整数,我们希望避免模偏差。假设我们从#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;
}
获得了一些随机的4位整数,但rand() % 10000
只能返回0到32767(就像在Microsoft Windows中一样)。 0到2767之间的每个数字看起来比2768到9999中的每个数字更频繁。要消除偏差,我们可以在值低于2768时重试rand()
,因为从2768到32767的30000值均匀地映射到10000个值,从0到9999。
对于浮点数,我们需要53个随机位,因为rand()
保持53位精度(假设它是IEEE的两倍)。如果我们使用超过53位,我们会得到舍入偏差。一些程序员编写类似double
的代码,但rand() / (double)RAND_MAX
可能只返回31位,或者只返回Windows中的15位。
OpenSSL的rand()
种子本身,也许是通过在Linux中阅读RAND_bytes()
。如果我们需要很多随机数,那么从/dev/urandom
读取它们就太慢了,因为它们必须从内核中复制出来。允许OpenSSL从种子生成更多随机数更快。
有关随机数的更多信息:
/dev/urandom
计算种子的示例。它混合了当前时间,进程ID和一些指针的位,如果它不能读取srand()
。答案 6 :(得分:9)
如果您的系统支持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
或类似文件,那么您只需更改传递给open
的路径在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;
}
答案 7 :(得分:8)
C不存在。您必须致电rand
,或者更好,random
。这些在标准库头stdlib.h
中声明。 rand
是POSIX,random
是BSD规范函数。
rand
和random
之间的区别在于random
返回更多可用的32位随机数,rand
通常返回16位数。 BSD联机帮助页显示rand
的低位是循环且可预测的,因此rand
对于小数字可能无用。
答案 8 :(得分:7)
查看ISAAC(间接,移位,累积,添加和计数)。它均匀分布,平均周期长度为2 ^ 8295。
答案 9 :(得分:4)
FWIW,答案是肯定的,有一个名为stdlib.h
的{{1}}函数;此功能主要针对速度和分布进行调整,而不是针对不可预测性。几乎所有用于各种语言和框架的内置随机函数都默认使用此函数。还有“加密”随机数生成器,它们的可预测性要低得多,但运行速度要慢得多。这些应该用于任何类型的安全相关应用程序。
答案 10 :(得分:4)
这是在您选择的两个数字之间获取随机数的好方法。
#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
之后的值更改为您选择的任何数字,并在这两个数字之间为您生成一个随机数。
答案 11 :(得分:4)
您想使用rand()
。注意(非常重要):确保为rand函数设置种子。如果不这样做,您的随机数不是真正的随机。这非常非常非常重要。值得庆幸的是,您通常可以使用系统滴答计时器和日期的某种组合来获得良好的种子。
答案 12 :(得分:3)
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]
答案 13 :(得分:3)
嗯,STL是C ++,而不是C,所以我不知道你想要什么。但是,如果您想要C,则有rand()
和srand()
函数:
int rand(void);
void srand(unsigned seed);
这些都是ANSI C的一部分。还有random()
函数:
long random(void);
但据我所知,random()
不是标准的ANSI C.第三方库可能不是一个坏主意,但这完全取决于你真正需要生成的数字的随机性。
答案 14 :(得分:2)
rand()
是生成随机数的最便捷方式。
您也可以通过random.org等任何在线服务获取随机数字。
答案 15 :(得分:2)
#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]);
}
答案 16 :(得分:1)
在现代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
答案 17 :(得分:1)
#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;
}
答案 18 :(得分:1)
#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;
}
答案 19 :(得分:0)
对于Linux C应用程序:
这是我根据上面的答案做的重做代码,它遵循我的C代码实践,并返回任意大小的随机缓冲区(带有正确的返回码等)。确保在程序开始时调用一次#include "testfunc.h"
std::vector<bool> funcToTest(const int &a, const int *b, int &c, int d)
{
std::vector<bool> out;
out.push_back(&a == b);
out.push_back(&c == b);
out.push_back(&d == b);
return out;
}
int main()
{
// put a breakpoint here, and step in, you would see that 'a'
// describes the situation as described above.
testfunc(funcToTest);
}
。
urandom_open()
答案 20 :(得分:0)
尽管所有人都在这里建议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()
函数,see here:
这个函数族应使用线性同余算法和48位整数运算生成伪随机数。
非常好的伪随机源是许多系统上可用的arc4random()
函数。不是任何官方标准的一部分,在1997年左右出现在BSD中,但你可以在Linux和macOS / iOS等系统上找到它。
答案 21 :(得分:0)
这是我的方法(rand()
的包装器):
我还进行了缩放以允许最小值为 INT_MIN
且最大值为 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);
定义和 doxygen 描述:
#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;
}
答案 22 :(得分:0)
我在最近的应用程序中遇到了一个严重的伪随机数生成器问题:我通过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是一个安全的赌注。
答案 23 :(得分:0)
听到一个很好的解释为什么使用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是个好主意。
答案 24 :(得分:-1)
您可以使用悬挂指针的概念。
指向已删除(或释放)的内存位置的指针称为悬空指针。
它将在打印时显示随机值。
答案 25 :(得分:-2)
我的简约解决方案适用于[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));
}
答案 26 :(得分:-3)
试试这个,我把它从上面提到的一些概念中加入:
/*
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;
}