【发布时间】:2010-10-23 18:24:15
【问题描述】:
在 C 中是否有生成随机 int 数的函数?还是我必须使用第三方库?
【问题讨论】:
在 C 中是否有生成随机 int 数的函数?还是我必须使用第三方库?
【问题讨论】:
注意:出于安全考虑,请勿使用
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。
【讨论】:
time() 每秒只更改一次。如果您从time() 播种,每次调用rand(),那么您将在一秒钟内为每次调用获得相同的值。但更大的原因是 rand() 的属性和类似的函数在每次运行时只播种一次的用例中最为人所知,而不是在每次调用时。依赖未经测试或未经证实的属性的“随机性”会导致麻烦。
rand()),使用rand() 播种充其量根本没有效果,最坏的情况是会破坏生成器的已知质量。这是一个深刻的课题。从阅读Knuth Vol 2 第 3 章开始,了解随机数,这是对数学和陷阱的最佳介绍。
srand((unsigned int)time(NULL));
<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 中所述,您需要执行以下操作之一:
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() 在密码学上是安全且无偏见的。
【讨论】:
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 从种子中生成更多随机数会更快。
更多关于随机数:
srand() 计算种子的示例。如果无法读取/dev/urandom,它将混合来自当前时间的位、进程 ID 和一些指针。【讨论】:
float/double 有额外解释的人,所以我已经澄清了这个问题,坚持使用int 数字以避免出现问题太宽泛。还有其他专门针对 float/double 随机值的 C 问题,因此您可能需要重新发布您对 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_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 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);。
查看ISAAC(间接、移位、累加、加和计数)。其分布均匀,平均循环长度为2^8295。
【讨论】:
这是在您选择的两个数字之间获取随机数的好方法。
#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()。注意(非常重要):确保为 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());
}
【讨论】:
生成 9 到 50 之间随机数的 C 程序
#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))
但是,因为:
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 是一个安全的选择。
【讨论】:
rand() 不应该用于加密数据,我同意。至少对我来说,我的应用程序不涉及加密数据,所以对我来说,给定的方法是可以的。
嗯,STL 是 C++,而不是 C,所以我不知道你想要什么。但是,如果您想要 C,则可以使用 rand() 和 srand() 函数:
int rand(void);
void srand(unsigned seed);
这些都是 ANSI C 的一部分。还有 random() 函数:
long random(void);
但据我所知,random() 不是标准的 ANSI C。第三方库可能不是一个坏主意,但这完全取决于您真正需要生成的数字的随机程度。
【讨论】:
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
【讨论】:
#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;
}
【讨论】:
尽管这里有很多人建议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 等系统上找到它。
【讨论】:
random() 在 Windows 上不存在。
rand(),因为它也是 C 标准所要求的。对于其他任何事情,您只需要一个适用于 Windows 的特殊解决方案,就像往常一样。 #ifdef _WIN32 是您在想要支持 Windows 的跨平台代码中最常看到的短语,并且通常有一种解决方案适用于所有系统,而另一种解决方案仅适用于 Windows。
#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。
【讨论】:
对于 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;
}
【讨论】:
如果您需要 128 个安全随机位,则符合 RFC 1750 的解决方案是读取已知会生成可用熵位的硬件源(例如旋转磁盘)。更好的是,好的实现应该使用混合函数组合多个来源,最后通过重新映射或删除输出去偏它们的输出分布。
如果您需要比这更多的位,则符合要求的做法是从 128 个安全随机位的序列开始,并将其拉伸到所需的长度,将其映射到人类可读的文本等。
如果你想在 C 语言中生成一个安全的随机数,我会按照这里的源代码:
请注意,Windows 使用的是 BCryptGenRandom,而不是 CryptGenRandom,后者在过去二十年中变得不安全。您可以自己确认 BCryptGenRandom 符合 RFC 1750。
对于 POSIX 兼容的操作系统,例如Ubuntu(Linux 的一种风格),您可以简单地从/dev/urandom 或/dev/random 中读取,这是一个类似于文件的设备接口,通过以符合 RFC 1750 的方式组合多个源来生成熵位。您可以使用read 或fread 从这些“文件”中读取所需数量的字节,就像读取任何其他文件一样,但请注意,从/dev/random 读取将阻塞,直到有足够的新熵位可用,而/dev/urandom 不会,这可能是一个安全问题。你可以通过检查可用熵池的大小来解决这个问题,或者我从entropy_avail 读取,或者使用ioctl。
【讨论】:
我的简约解决方案应该适用于[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));
}
【讨论】:
您可以使用悬空指针的概念。
指向已被删除(或释放)的内存位置的指针称为悬空指针。
打印时会显示随机值。
【讨论】:
试试这个,我把上面已经提到的一些概念放在一起:
/*
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() 时都打电话给srand() 是一个糟糕的主意。由于time() 通常会在 秒 内返回一个值,因此快速调用此函数将返回相同的“随机”值。
random()函数混淆。