Existe uma função para gerar um número int aleatório em C? Ou precisarei usar uma biblioteca de terceiros?
srand
: por que chamá-lo apenas uma vez .
Existe uma função para gerar um número int aleatório em C? Ou precisarei usar uma biblioteca de terceiros?
srand
: por que chamá-lo apenas uma vez .
Respostas:
Nota : Não use
rand()
por segurança. Se você precisar de um número criptograficamente seguro, consulte esta resposta .
#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.
Edit : No Linux, você pode preferir usar random e srandom .
time()
apenas muda uma vez por segundo. Se você inicializar time()
, para cada chamada para rand()
, obterá o mesmo valor para cada chamada durante um único segundo. Mas a razão maior é que as propriedades rand()
e funções como elas são conhecidas melhor para o caso de uso em que elas são propagadas exatamente uma vez por execução, e não em todas as chamadas. Dependendo da "aleatoriedade" com propriedades não testadas ou não comprovadas, leva a problemas.
rand()
geralmente é) semear rand()
na melhor das hipóteses não teria nenhum efeito e, na pior das hipóteses, romperia as qualidades conhecidas do gerador. Este é um assunto profundo. Comece lendo o capítulo 3 de Knuth Vol 2, sobre números aleatórios, como a melhor introdução à matemática e às armadilhas.
srand((unsigned int)time(NULL));
A rand()
função in <stdlib.h>
retorna um número inteiro pseudo-aleatório entre 0 e RAND_MAX
. Você pode usar srand(unsigned int seed)
para definir uma semente.
É prática comum usar o %
operador em conjunto rand()
para obter um intervalo diferente (embora tenha em mente que isso diminui um pouco a uniformidade). Por exemplo:
/* random int between 0 and 19 */
int r = rand() % 20;
Se você realmente se preocupa com a uniformidade, pode fazer algo assim:
/* 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;
}
}
%
é o operador de módulo. Ele fornece o restante de uma divisão inteira, portanto x % n
sempre fornecerá um número entre 0
e n - 1
(contanto que x
e n
sejam ambos positivos). Se você ainda achar que confuso, tente escrever um programa que tem i
contar de 0 a 100, e imprime i % n
por alguma n
de sua escolha menor do que 100.
Conforme abordado em como gerar números aleatórios com segurança em várias linguagens de programação , você deve seguir um destes procedimentos:
randombytes
/dev/urandom
, não /dev/random
. Não é OpenSSL (ou outros PRNGs do espaço do usuário).Por exemplo:
#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()
é criptograficamente seguro e imparcial.
sodium_init()
em algum momento. Não se preocupe com o RNG, ele usa o kernel.
sodium_init()
mesmo que não seja necessariamente parte do meu exemplo, porque é um detalhe importante.
Vamos passar por isso. Primeiro, usamos a função srand () para propagar o randomizador. Basicamente, o computador pode gerar números aleatórios com base no número que é alimentado para srand (). Se você der o mesmo valor inicial, os mesmos números aleatórios serão gerados sempre.
Portanto, temos que propagar o randomizador com um valor que está sempre mudando. Fazemos isso alimentando o valor do tempo atual com a função time ().
Agora, quando chamamos rand (), um novo número aleatório será produzido toda vez.
#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) falha quando hi_num - low_num > INT_MAX
. 3) Omite valores em situações raras INT_MAX > hi_num - low_num > RAND_MAX
.
hi_num = max_num + 1;
falta proteção contra transbordamento.
Se você precisar de números pseudo-aleatórios de melhor qualidade do que o stdlib
fornecido, consulte Mersenne Twister . Também é mais rápido. As implementações de amostra são abundantes, por exemplo aqui .
A função C padrão é rand()
. É bom o suficiente para distribuir cartas de paciência, mas é horrível. Muitas implementações de rand()
ciclo passam por uma pequena lista de números e os bits baixos têm ciclos mais curtos. A maneira como alguns programas chamam rand()
é terrível, e srand()
é difícil calcular uma boa semente para a qual passar .
A melhor maneira de gerar números aleatórios em C é usar uma biblioteca de terceiros como o OpenSSL. Por exemplo,
#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;
}
Por que tanto código? Outras linguagens como Java e Ruby têm funções para números inteiros ou flutuantes aleatórios. O OpenSSL fornece apenas bytes aleatórios, então tento imitar como o Java ou Ruby os transformaria em números inteiros ou flutuantes.
Para números inteiros, queremos evitar viés de módulo . Suponha que obtivemos números inteiros aleatórios de 4 dígitos rand() % 10000
, mas rand()
só podemos retornar de 0 a 32767 (como ocorre no Microsoft Windows). Cada número de 0 a 2767 seria exibido com mais frequência do que cada número de 2768 a 9999. Para remover o viés, podemos tentar novamente rand()
enquanto o valor estiver abaixo de 2768, porque os valores de 30000 de 2768 a 32767 são mapeados uniformemente nos valores de 10000 de 0 a 9999
Para carros alegóricos, queremos 53 bits aleatórios, porque um double
detém 53 bits de precisão (supondo que seja um duplo IEEE). Se usarmos mais de 53 bits, obteremos um viés de arredondamento. Alguns programadores escrevem código como rand() / (double)RAND_MAX
, mas rand()
podem retornar apenas 31 bits ou apenas 15 bits no Windows.
O OpenSSL se propaga RAND_bytes()
, talvez lendo /dev/urandom
no Linux. Se precisarmos de muitos números aleatórios, seria muito lento ler todos eles /dev/urandom
, porque eles devem ser copiados do kernel. É mais rápido permitir que o OpenSSL gere mais números aleatórios a partir de uma semente.
Mais sobre números aleatórios:
srand()
. Ele mistura bits do horário atual, a identificação do processo e alguns ponteiros, se não puder ler /dev/urandom
.float
/ double
, por isso esclarei a questão de manter os int
números para evitar que seja muito ampla. Há outras questões C que tratam especificamente float
/ double
valores aleatórios, de modo que você pode querer repassar sua segunda metade de sua resposta a perguntas como stackoverflow.com/questions/13408990/...
Se o seu sistema suportar a arc4random
família de funções, eu recomendaria usá-las na rand
função padrão .
A arc4random
família inclui:
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
retorna um número inteiro não assinado aleatório de 32 bits.
arc4random_buf
coloca conteúdo aleatório no seu parâmetro buf : void *
. A quantidade de conteúdo é determinada pelo bytes : size_t
parâmetro
arc4random_uniform
retorna um número inteiro não assinado aleatório de 32 bits que segue a regra 0 <= arc4random_uniform(limit) < limit
:, em que limit também é um número inteiro não assinado de 32 bits.
arc4random_stir
lê dados /dev/urandom
e passa os dados para arc4random_addrandom
randomizar adicionalmente seu pool de números aleatórios interno.
arc4random_addrandom
é usado arc4random_stir
para preencher seu pool de números aleatórios interno de acordo com os dados passados para ele.
Se você não possui essas funções, mas está no Unix, pode usar este código:
/* 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;
}
A urandom_init
função abre o /dev/urandom
dispositivo e coloca o descritor de arquivo urandom_fd
.
A urandom
função é basicamente a mesma que uma chamada para rand
, exceto mais segura, e retorna a long
(facilmente alterável).
No entanto, /dev/urandom
pode ser um pouco lento, por isso é recomendável usá-lo como uma semente para um gerador de números aleatórios diferente.
Se o seu sistema não tem um /dev/urandom
, mas não têm um /dev/random
arquivo ou similar, então você pode simplesmente mudar o caminho passado para open
nos urandom_init
. As chamadas e APIs usadas urandom_init
e urandom
(acredito) compatíveis com POSIX e, como tal, devem funcionar na maioria, se não em todos os sistemas compatíveis com POSIX.
Notas: Uma leitura de /dev/urandom
NÃO bloqueará se houver entropia insuficiente disponível; portanto, os valores gerados nessas circunstâncias podem ser criptograficamente inseguros. Se você estiver preocupado com isso, use o /dev/random
que sempre bloqueará se a entropia for insuficiente.
Se você estiver em outro sistema (por exemplo, Windows), use rand
ou alguma API não portátil, dependente da plataforma, específica do Windows.
Wrapper função para urandom
, rand
ou arc4random
chamadas:
#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */
int myRandom(int bottom, int top){
return (RAND_IMPL() % (top - bottom)) + bottom;
}
STL não existe para C. Você precisa ligar rand
, ou melhor ainda random
,. Eles são declarados no cabeçalho da biblioteca padrão stdlib.h
. rand
é POSIX, random
é uma função de especificação BSD.
A diferença entre rand
e random
é que random
retorna um número aleatório de 32 bits muito mais utilizável e rand
geralmente retorna um número de 16 bits. As páginas de manual do BSD mostram que os bits mais baixos rand
são cíclicos e previsíveis, portanto, rand
são potencialmente inúteis para pequenos números.
extern int rand(void);
e extern void srand(unsigned int);
.
Dê uma olhada no ISAAC (Indirection, Shift, Accumulate, Add e Count). É uniformemente distribuído e tem um ciclo médio de 2 ^ 8295.
Essa é uma boa maneira de obter um número aleatório entre dois números de sua escolha.
#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));
}
Saída pela primeira vez: 39
Saída pela segunda vez: 61
Saída pela terceira vez: 65
Você pode alterar os valores depois randnum
para qualquer número que escolher, e isso gerará um número aleatório para você entre esses dois números.
Você quer usar rand()
. Nota ( MUITO IMPORTANTE ): certifique-se de definir a semente para a função rand. Caso contrário, seus números aleatórios não são verdadeiramente aleatórios . Isso é muito, muito, muito importante. Felizmente, você geralmente pode usar alguma combinação do cronômetro de ticks do sistema e da data para obter uma boa semente.
FWIW, a resposta é que sim, existe uma stdlib.h
função chamada rand
; essa função é ajustada principalmente para velocidade e distribuição, não para imprevisibilidade. Quase todas as funções aleatórias internas para vários idiomas e estruturas usam essa função por padrão. Também existem geradores de números aleatórios "criptográficos" que são muito menos previsíveis, mas que executam muito mais lentamente. Eles devem ser usados em qualquer tipo de aplicativo relacionado à segurança.
Espero que isso seja um pouco mais aleatório do que apenas usar 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());
}
Bem, STL é C ++, não C, então não sei o que você deseja. Se você deseja C, no entanto, existem as funções rand()
e srand()
:
int rand(void);
void srand(unsigned seed);
Ambos fazem parte do ANSI C. Há também a random()
função:
long random(void);
Mas, pelo que sei, random()
não é o padrão ANSI C. Uma biblioteca de terceiros pode não ser uma má idéia, mas tudo depende de quão aleatório é o número que você realmente precisa gerar.
Programa C para gerar números aleatórios entre 9 e 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);
}
Em geral, podemos gerar um número aleatório entre lowerLimit e upperLimit-1
ou seja, lowerLimit é inclusivo ou diz r ∈ [lowerLimit, upperLimit)
rand()
é a maneira mais conveniente de gerar números aleatórios.
Você também pode pegar um número aleatório em qualquer serviço online como o 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]);
}
#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;
}
Nos modernos processadores x86_64, você pode usar o gerador de números aleatórios de hardware via _rdrand64_step()
Código de exemplo:
#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<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;
}
Ouvir uma boa explicação de por que usar rand()
para produzir números aleatórios distribuídos uniformemente em um determinado intervalo é uma má idéia, decidi dar uma olhada no quão distorcida a saída realmente é. Meu caso de teste foi arriscado. Aqui está o código 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);
}
e aqui está sua saída:
$ 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%
Não sei o quão uniforme você precisa que seus números aleatórios sejam, mas o acima parece uniforme o suficiente para a maioria das necessidades.
Edit: seria uma boa idéia inicializar o PRNG com algo melhor que time(NULL)
.
Eu tive um problema sério com o gerador de números pseudo-aleatórios em meu aplicativo recente: chamei repetidamente meu programa C por meio de um script pyhton e estava usando como semente o seguinte código:
srand(time(NULL))
No entanto, desde:
man srand
);time
retornará o mesmo valor a cada vez.Meu programa gerou a mesma sequência de números. Você pode fazer três coisas para resolver esse problema:
misture a saída do tempo com outras informações alteradas nas execuções (no meu aplicativo, o nome da saída):
srand(time(NULL) | getHashOfString(outputName))
Eu usei o djb2 como minha função hash.
Aumente a resolução do tempo. Na minha plataforma, clock_gettime
estava disponível, então eu o uso:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec);
Use os dois métodos juntos:
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec | getHashOfString(outputName));
A opção 3 garante a você (tanto quanto eu sei) a melhor aleatoriedade de sementes, mas pode criar uma diferença apenas em aplicativos muito rápidos. Na minha opinião, a opção 2 é uma aposta segura.
rand()
não deve ser usado para dados criptográficos, eu concordo. Pelo menos para mim, meu aplicativo não envolvia dados criptográficos, portanto, para mim, estava ok o método fornecido.
Apesar de todas as sugestões de pessoas rand()
aqui, você não deseja usar a rand()
menos que precise! Os números aleatórios que rand()
produz são frequentemente muito ruins. Para citar a página de manual do Linux:
As versões
rand()
esrand()
na Biblioteca C do Linux usam o mesmo gerador de números aleatórios querandom(3)
esrandom(3)
, portanto, os bits de ordem inferior devem ser tão aleatórios quanto os bits de ordem superior. No entanto, em implementações rand () mais antigas e em implementações atuais em sistemas diferentes, os bits de ordem inferior são muito menos aleatórios que os bits de ordem superior . Não use essa função em aplicativos destinados a serem portáteis quando for necessária uma boa aleatoriedade. ( Use emrandom(3)
vez disso. )
Em relação à portabilidade, random()
também é definido pelo padrão POSIX há algum tempo. rand()
é mais antigo, já apareceu na primeira especificação POSIX.1 (IEEE Std 1003.1-1988), enquanto random()
apareceu pela primeira vez no POSIX.1-2001 (IEEE Std 1003.1-2001), mas o padrão atual do POSIX já é POSIX.1-2008 (IEEE Std 1003.1-2008), que recebeu uma atualização há apenas um ano (IEEE Std 1003.1-2008, edição de 2016). Então eu considerariarandom()
muito portátil.
O POSIX.1-2001 também introduziu as funções lrand48()
e mrand48()
, veja aqui :
Essa família de funções deve gerar números pseudo-aleatórios usando um algoritmo congruencial linear e aritmética de número inteiro de 48 bits.
E uma boa fonte pseudo-aleatória é a arc4random()
função que está disponível em muitos sistemas. Não faz parte de nenhum padrão oficial, apareceu no BSD por volta de 1997, mas você pode encontrá-lo em sistemas como Linux e macOS / iOS.
random()
não existe no Windows.
rand()
como também é exigido pelo padrão C. Para qualquer outra coisa, você precisa de uma solução especial apenas para Windows, como de costume. #ifdef _WIN32
é a frase que você verá com mais frequência no código de plataforma cruzada que deseja oferecer suporte ao Windows e, geralmente, existe uma solução que funciona com todos os sistemas e uma que é necessária apenas para o Windows.
Para aplicativos Linux C:
Este é o meu código reformulado a partir de uma resposta acima que segue minhas práticas de código C e retorna um buffer aleatório de qualquer tamanho (com códigos de retorno adequados, etc.). Certifique-se de ligar urandom_open()
uma vez no início do seu programa.
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;
}
}
Minha solução minimalista deve funcionar para números aleatórios no intervalo [min, max)
. Use srand(time(NULL))
antes de chamar a função.
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));
}
Tente isso, juntei alguns dos conceitos já mencionados acima:
/*
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;
}
srand()
sempre que quiser telefonar rand()
é uma péssima idéia. Como time()
normalmente retorna um valor em segundos, chamando esta função rapidamente retornará o mesmo valor "aleatório".
random()
função do Unix .