是否有在 C 中生成随机 int 数的函数?还是我必须使用第三方库?
注意:不要使用 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 and srandom。
<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;
}
}
%
是模数运算符。它为您提供整数除法的余数,因此 x % n
将始终为您提供介于 0
和 n - 1
之间的数字(只要 x
和 n
都是正数)。如果您仍然感到困惑,请尝试编写一个程序,将 i
计数从 0 到 100,并为您选择的一些小于 100 的 n
打印出 i % n
。
如果您需要安全的随机字符或整数:
如 how to safely generate random numbers in various programming languages 中所述,您需要执行以下操作之一:
使用 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()
在密码学上是安全且公正的。
sodium_init()
。不用担心 RNG,它使用内核的。
sodium_init()
的修改,尽管它不一定是我示例的一部分,因为它是一个重要的细节。
RAND_bytes()
的文档说它是一种加密安全的 PRNG。
让我们来看看这个。首先,我们使用 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;
}
else{ low_num=max_num; hi_num=min_num+1;
2) 在 hi_num - low_num > INT_MAX
时失败。 3) 在罕见情况 INT_MAX > hi_num - low_num > RAND_MAX
中省略值。
hi_num = max_num + 1;
缺乏防止溢出的保护。
如果您需要比 stdlib
提供的质量更好的伪随机数,请查看 Mersenne Twister。它也更快。示例实现很丰富,例如 here。
标准 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 从种子中生成更多随机数会更快。
更多关于随机数:
Perl 的 Perl_seed() 是如何在 C 中为 srand() 计算种子的示例。如果它无法读取 /dev/urandom,它会混合来自当前时间的位、进程 ID 和一些指针。
OpenBSD 的 arc4random_uniform() 解释了模偏差。
java.util.Random 的 Java API 描述了从随机整数中消除偏差并将 53 位打包成随机浮点数的算法。
float
/double
有额外解释的人,因此我已经澄清了这个问题,坚持使用 int
数字以避免出现问题太宽泛。还有其他 C 问题专门处理 float
/double
随机值,因此您可能需要重新发布您对 stackoverflow.com/questions/13408990/… 等问题的回答的后半部分
如果您的系统支持 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_stir
使用 arc4random_addrandom
根据传递给它的数据填充其内部随机数池。
如果您没有这些功能,但您使用的是 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
的调用基本相同,只是更安全,它返回一个 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;
}
C 不存在 STL。您必须调用 rand
,或者更好的是,调用 random
。这些在标准库头文件 stdlib.h
中声明。 rand
是 POSIX,random
是 BSD 规范函数。
rand
和 random
之间的区别在于 random
返回一个更有用的 32 位随机数,而 rand
通常返回一个 16 位数字。 BSD 联机帮助页显示 rand
的低位是循环且可预测的,因此 rand
对于小数可能无用。
extern int rand(void);
和 extern void srand(unsigned int);
。
这是在您选择的两个数字之间获取随机数的好方法。
#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
之后的值更改为您选择的任何数字,它会在这两个数字之间为您生成一个随机数。
好吧,STL 是 C++,而不是 C,所以我不知道你想要什么。但是,如果您想要 C,则有 rand()
和 srand()
函数:
int rand(void);
void srand(unsigned seed);
这些都是 ANSI C 的一部分。还有 random()
函数:
long random(void);
但据我所知,random()
不是标准的 ANSI C。第三方库可能不是一个坏主意,但这完全取决于您真正需要生成的数字的随机程度。
您想使用 rand()
。注意(非常重要):确保为 rand 函数设置种子。否则,您的随机数就不是真正随机的。这是非常非常非常重要的。值得庆幸的是,您通常可以使用系统滴答计时器和日期的某种组合来获得好种子。
FWIW,答案是肯定的,有一个名为 rand
的 stdlib.h
函数;此功能主要针对速度和分布进行调整,而不是针对不可预测性。几乎所有各种语言和框架的内置随机函数都默认使用此函数。还有一些“加密”随机数生成器,它们的可预测性要低得多,但运行速度要慢得多。这些应该用于任何类型的安全相关应用程序。
希望这比仅使用 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());
}
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 )
在我最近的应用程序中,我遇到了一个伪随机数生成器的严重问题:我通过 Python 脚本反复调用我的 C 程序,并使用以下代码作为种子:
srand(time(NULL))
但是,由于:
rand 将生成相同的伪随机序列,在 srand 中给出相同的种子(参见 man srand);
如前所述,时间函数仅在秒之间发生变化:如果您的应用程序在同一秒内多次运行,则时间每次都将返回相同的值。
我的程序生成了相同的数字序列。你可以做 3 件事来解决这个问题:
将时间输出与运行时更改的其他一些信息混合在一起(在我的应用程序中,输出名称): srand(time(NULL) | getHashOfString(outputName)) 我使用 djb2 作为我的哈希函数。提高时间分辨率。在我的平台上,clock_gettime 可用,所以我使用它:#include
选项 3 确保您(据我所知)最好的种子随机性,但它可能只会在非常快速的应用程序上产生差异。在我看来,选项 2 是一个安全的选择。
rand()
不应用于加密数据,我同意。至少对我来说,我的应用程序不涉及加密数据,所以对我来说,给定的方法是可以的。
rand()
是生成随机数的最便捷方式。
您还可以从任何在线服务(如 random.org)中捕获随机数。
#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
的实现。
在现代 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
if
。如果多个线程快速提取随机数,则实际 CPU 上会出现临时故障。有关更好的包装函数,请参阅 RDRAND and RDSEED intrinsics on various compilers?。
尽管这里有很多人建议 rand()
,但除非必须,否则您不想使用 rand()
! rand()
产生的随机数通常非常糟糕。引用 Linux 手册页:
Linux C 库中的 rand() 和 srand() 版本使用与 random(3) 和 srandom(3) 相同的随机数生成器,因此低位应该与高位一样随机。但是,在较旧的 rand() 实现中,以及在不同系统上的当前实现中,低阶位的随机性远低于高阶位。当需要良好的随机性时,请勿在旨在可移植的应用程序中使用此功能。 (使用 random(3) 代替。)
关于可移植性,POSIX 标准也定义了 random()
很长一段时间。 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 等系统上找到它。
random()
在 Windows 上不存在。
rand()
,因为它也是 C 标准的要求。对于其他任何事情,您都需要一个仅适用于 Windows 的特殊解决方案,就像往常一样。 #ifdef _WIN32
是您在想要支持 Windows 的跨平台代码中最常看到的短语,并且通常有一种解决方案适用于所有系统,而另一种解决方案仅适用于 Windows。
#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>
//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;
}
听到一个很好的解释为什么使用 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。
%6
表示您的随机性包括较高的位,而不仅仅是像 %8
那样的低位。因此,避免了基于 LCG 的 rand()
的一大缺点。当然,LCG 的大问题不是长期的整体分布,而是像低位每次从奇数到偶数交替,对于一个非常简单的 LCG。将计数器从 0 增加到 n 也会提供非常均匀的分布,但不是随机的。所以你的测试没有区分随机序列和近乎线性的序列,因此不能告诉我们很多类型的潜在问题,只有偏差。
对于 Linux C 应用程序:
这是我从上面的答案中修改的代码,它遵循我的 C 代码实践并返回任意大小的随机缓冲区(具有正确的返回代码等)。确保在程序开始时调用一次 urandom_open()
。
int gUrandomFd = -1;
int urandom_open(void)
{
if (gUrandomFd == -1) {
gUrandomFd = open("/dev/urandom", O_RDONLY);
}
if (gUrandomFd == -1) {
fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
errno, strerror(errno));
return -1;
} else {
return 0;
}
}
void urandom_close(void)
{
close(gUrandomFd);
gUrandomFd = -1;
}
//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
int ret = 0; // Return value
if (gUrandomFd == -1) {
fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
return -1;
}
ret = read(gUrandomFd, buf, size);
if (ret != size) {
fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
ret, size);
return -1;
} else {
return 0;
}
}
这是我的方法(围绕 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);
定义和 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;
}
也可以看看:
[我在上面写下我的答案后发现了这个问答,但它显然非常相关,并且他们对非缩放范围的情况做同样的事情]如何从 rand() 获得特定范围的数字? [我还需要学习和阅读这个答案——似乎有一些关于通过不单独使用模数来保持良好随机性的好处] 如何从 rand() 获得特定范围的数字? http://c-faq.com/lib/randrange.html
例如,如果您需要 128 个安全随机位,则符合 RFC 1750 的解决方案是读取已知会生成可用熵位的硬件源(例如旋转磁盘)。更好的是,好的实现应该使用混合函数组合多个源,最后通过重新映射或删除输出来消除它们的输出分布。
如果您需要比这更多的位,那么合规的做法是从 128 个安全随机位的序列开始,并将其拉伸到所需的长度,将其映射到人类可读的文本等。
如果您想在 CI 中生成一个安全的随机数,请遵循此处的源代码:
请注意,对于 Windows,使用的是 BCryptGenRandom,而不是 CryptGenRandom,后者在过去的二十年中变得不安全。您可以自己确认 BCryptGenRandom 符合 RFC 1750。
对于 POSIX 兼容的操作系统,例如 Ubuntu(Linux 的一种风格),您可以简单地从 /dev/urandom
或 /dev/random
读取,这是一个类似于文件的设备接口,通过在一个文件中组合多个源来生成熵位符合 RFC 1750 的时尚。您可以使用 read
或 fread
从这些“文件”中读取所需数量的字节,就像读取任何其他文件一样,但请注意,从 /dev/random
读取将阻塞,直到有足够的新熵位可用,而/dev/urandom
不会,这可能是一个安全问题。您可以通过检查可用熵池的大小(我从 entropy_avail
读取的内容或使用 ioctl
)来解决这个问题。
与此相关的 glibc 特定函数(应在大多数 Linux 环境中找到)是 random()
,或者您可能对它的线程安全版本 random_r()
感兴趣。在将 struct 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);
}
}
您可以生成随机字符,然后将它们视为 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 ;
}
我的简约解决方案应该适用于 [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));
}
您可以使用悬空指针的概念。
指向已被删除(或释放)的内存位置的指针称为悬空指针。
它会在打印时显示随机值。
time()
每秒只更改一次。如果您从time()
播种,对于对rand()
的每次调用,您将在一秒钟内为每次调用获得相同的值。但更大的原因是rand()
的属性和类似的函数在每次运行时只播种一次的用例中最为人所知,而不是在每次调用时。依赖未经测试或未经证实的属性的“随机性”会导致麻烦。rand()
),使用rand()
播种充其量根本没有效果,最坏的情况会破坏生成器的已知质量。这是一个深刻的课题。首先阅读Knuth Vol 2第 3 章关于随机数的内容,这是对数学和陷阱的最佳介绍。srand((unsigned int)time(NULL));