Łuk*_*Lew 642
注意:请勿
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.
Run Code Online (Sandbox Code Playgroud)
编辑:在Linux上,您可能更喜欢使用random和srandom.
Lau*_*ves 231
该rand()函数<stdlib.h>返回0之间的伪随机整数RAND_MAX.您可以使用srand(unsigned int seed)设置种子.
通常的做法%是结合使用操作符rand()来获得不同的范围(但请记住,这会在某种程度上抛弃均匀性).例如:
/* random int between 0 and 19 */
int r = rand() % 20;
Run Code Online (Sandbox Code Playgroud)
如果你真的关心一致性,你可以这样做:
/* 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;
}
}
Run Code Online (Sandbox Code Playgroud)
Sco*_*ski 52
正如在讨论如何安全地生成各种编程语言的随机数,你会想要做下列之一:
randombytesAPI/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);
}
Run Code Online (Sandbox Code Playgroud)
randombytes_uniform() 密码安全且无偏见.
小智 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;
}
Run Code Online (Sandbox Code Playgroud)
MH1*_*114 25
如果您需要比stdlib提供的质量更好的伪随机数,请查看Mersenne Twister.它也更快.示例实现很多,例如这里.
Geo*_*ler 17
标准的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;
}
Run Code Online (Sandbox Code Playgroud)
为什么这么多代码?Java和Ruby等其他语言具有随机整数或浮点数的函数.OpenSSL只提供随机字节,因此我尝试模仿Java或Ruby如何将它们转换为整数或浮点数.
对于整数,我们希望避免模偏差.假设我们从中获得了一些随机的4位整数rand() % 10000,但rand()只能返回0到32767(就像在Microsoft Windows中一样).从27到9999中,每个数字从0到2767的出现频率会更高.为了消除偏差,我们可以rand()在值低于2768时重试,因为从2768到32767的30000值均匀映射到从0到0的10000个值9999.
对于浮点数,我们需要53个随机位,因为a double保持53位精度(假设它是IEEE的两倍).如果我们使用超过53位,我们会得到舍入偏差.有些程序员编写类似的代码rand() / (double)RAND_MAX,但rand()在Windows中可能只返回31位或15位.
OpenSSL的RAND_bytes()种子本身,也许是通过阅读/dev/urandomLinux.如果我们需要许多随机数,那么从它们全部读取它们会太慢/dev/urandom,因为它们必须从内核中复制.允许OpenSSL从种子生成更多随机数更快.
更多关于随机数字:
srand().它混合来自当前时间,进程ID和一些指针的位,如果它不能读取/dev/urandom.Éle*_*tra 10
如果您的系统支持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)
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
该urandom_init函数打开/dev/urandom设备,并将文件描述符放入urandom_fd.
该urandom函数与调用基本相同rand,除了更安全,它返回一个long(容易更改).
但是,/dev/urandom可能有点慢,因此建议您将其用作不同随机数生成器的种子.
如果您的系统没有/dev/urandom,但确实有一个/dev/random或类似的文件,那么你可以简单地改变传递到路径open在urandom_init.该调用和API中使用的urandom_init和urandom是(我相信)兼容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;
}
Run Code Online (Sandbox Code Playgroud)
C不存在.你必须打电话rand,或者更好random.这些是在标准库头中声明的stdlib.h.rand是POSIX,random是BSD规范函数.
rand和之间的区别random是random返回一个更加可用的32位随机数,rand通常返回一个16位数.BSD联机帮助页显示较低位rand是循环且可预测的,因此rand对于小数字可能无用.
小智 5
这是在您选择的两个数字之间获得随机数的好方法。
#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));
}
Run Code Online (Sandbox Code Playgroud)
第一次输出:39
第二次输出:61
第三次输出:65
您可以将后面的值更改为randnum您选择的任何数字,它会在这两个数字之间为您生成一个随机数。
我在最近的应用程序中遇到了伪随机数生成器的严重问题:我通过 Python 脚本反复调用我的 C 程序,并使用以下代码作为种子:
srand(time(NULL))
Run Code Online (Sandbox Code Playgroud)
然而,因为:
man srand);time每次都会返回相同的值。我的程序生成了相同的数字序列。您可以做 3 件事来解决这个问题:
将时间输出与运行时更改的一些其他信息混合(在我的应用程序中,输出名称):
srand(time(NULL) | getHashOfString(outputName))
Run Code Online (Sandbox Code Playgroud)
我使用djb2作为我的哈希函数。
提高时间分辨率。在我的平台上,clock_gettime可用,所以我使用它:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec);
Run Code Online (Sandbox Code Playgroud)
一起使用这两种方法:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec | getHashOfString(outputName));
Run Code Online (Sandbox Code Playgroud)
选项 3 可确保您(据我所知)获得最佳的种子随机性,但它可能仅在非常快的应用程序上产生差异。我认为选项 2 是一个安全的选择。
| 归档时间: |
|
| 查看次数: |
1543631 次 |
| 最近记录: |