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 % nsempre fornecerá um número entre 0e n - 1(contanto que xe nsejam ambos positivos). Se você ainda achar que confuso, tente escrever um programa que tem icontar de 0 a 100, e imprime i % npor alguma nde 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 stdlibfornecido, 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 doubledeté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/urandomno 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 intnúmeros para evitar que seja muito ampla. Há outras questões C que tratam especificamente float/ doublevalores 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 arc4randomfamília de funções, eu recomendaria usá-las na randfunção padrão .
A arc4randomfamí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_bufcoloca conteúdo aleatório no seu parâmetro buf : void *. A quantidade de conteúdo é determinada pelo bytes : size_tparâmetro
arc4random_uniformretorna 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_stirlê dados /dev/urandome passa os dados para arc4random_addrandomrandomizar adicionalmente seu pool de números aleatórios interno.
arc4random_addrandomé usado arc4random_stirpara 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_initfunção abre o /dev/urandomdispositivo e coloca o descritor de arquivo urandom_fd.
A urandomfunção é basicamente a mesma que uma chamada para rand, exceto mais segura, e retorna a long(facilmente alterável).
No entanto, /dev/urandompode 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/randomarquivo ou similar, então você pode simplesmente mudar o caminho passado para opennos urandom_init. As chamadas e APIs usadas urandom_inite 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/urandomNÃ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/randomque sempre bloqueará se a entropia for insuficiente.
Se você estiver em outro sistema (por exemplo, Windows), use randou alguma API não portátil, dependente da plataforma, específica do Windows.
Wrapper função para urandom, randou arc4randomchamadas:
#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 rande randomé que randomretorna um número aleatório de 32 bits muito mais utilizável e randgeralmente retorna um número de 16 bits. As páginas de manual do BSD mostram que os bits mais baixos randsão cíclicos e previsíveis, portanto, randsã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 randnumpara 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.hfunçã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);timeretornará 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_gettimeestava 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 .