Como gerar um int aleatório em C?


Respostas:


663

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 .


194
+1 por simplicidade, mas provavelmente é uma boa idéia enfatizar que srand () deve ser chamado apenas uma vez . Além disso, em um aplicativo encadeado, convém garantir que o estado do gerador seja armazenado por encadeamento e propagar o gerador uma vez para cada encadeamento.
RBerteig

41
@trusktr, é complicado. Aqui está uma razão: 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.
RBerteig 01/10/12

9
@trusktr para um gerador congruencial linear simples (que é o que 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.
RBerteig 03/10/2012

21
Evite um aviso do compilador com um elenco:srand((unsigned int)time(NULL));
GiovaMaster 14/10

9
Lembre-se de que essa ainda é uma maneira fraca de ver o PRNG. No ano passado, um vírus do tipo cryptolocker no Linux cometeu o erro de se espalhar com o tempo, e isso reduziu drasticamente o espaço de pesquisa. Tudo o que você precisava fazer era ter uma idéia decente de quando a infecção ocorreu e, em seguida, tentar as sementes da época. A última vez que ouvi falar, a melhor fonte de aleatoriedade é / dev / urandom, que supostamente é semeada a partir de um mashup de fontes caóticas, como temperaturas no hardware. Se tudo o que você realmente deseja, no entanto, é que seu programa atue de maneira diferente em cada execução, a solução acima é adequada.
Jemenake

238

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;
  }
}

18
É uma prática comum , mas não a correta. Veja isso e isso .
Lazer

35
@Lazer: É por isso que eu disse "embora tenha em mente que isso diminui um pouco a uniformidade".
Laurence Gonsalves

3
@AbhimanyuAryan O %é 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.
Laurence Gonsalves

2
@necromancer Fui em frente e adicionei uma solução perfeitamente uniforme.
Laurence Gonsalves

2
@Lazer, o segundo link que você postou ainda não é perfeitamente uniforme. Converter para o dobro e para trás não ajuda. O primeiro link que você postou tem uma solução perfeitamente uniforme, embora faça um loop muito por pequenos limites superiores. Eu adicionei uma solução perfeitamente uniforme a essa resposta que não deve ser repetida tanto em pequenos limites superiores.
Laurence Gonsalves

53

Se você precisar de caracteres aleatórios seguros ou números inteiros:

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:

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.


o RNG libsódico deve ser semeado antes de chamar randombytes_buf?
user2199593

Basta ligar sodium_init()em algum momento. Não se preocupe com o RNG, ele usa o kernel.
Scott Arciszewski

Nota: eu aprovei a edição recente, sodium_init()mesmo que não seja necessariamente parte do meu exemplo, porque é um detalhe importante.
22418 Scott Arciszewski

29

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;
}

12
Código legal, mas não é uma boa idéia chamar 'srand (time (NULL));'. esse método produz o mesmo número quando chamado em um loop for.
precisa saber é o seguinte

1
As edições sugeridas que envolvem código geralmente são rejeitadas. Alguém fez um aqui com o comentário "o algoritmo estava errado. Poderia produzir números maiores que o máximo". Ainda não avaliei a reivindicação.
Martin Smith

1
@ Martin Smith Problemas: 1) deve ser 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.
chux - Restabelece Monica

1
Reseedá-lo dessa maneira fará com que essa função produza o mesmo número se for chamada várias vezes no mesmo segundo. Se você realmente deseja realimentá-lo novamente, repita o processo apenas uma vez por segundo.
Elektra

1
Menor: hi_num = max_num + 1;falta proteção contra transbordamento.
chux - Restabelece Monica 8/8

25

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 .


2
+1: Parece legal, mas eu estava apenas fazendo um jogo de adivinhação. Se eu fosse usar um gerador de números aleatórios em um aplicativo de negócios, eu definitivamente usaria isso.
Kredns

4
Não use um Mersenne Twister, use algo bom como xoroshiro128 + ou PCG. (Link relevante).
Veedrac

17

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:


Obrigado por esta resposta estendida. Observe que das 24 respostas atuais a esta pergunta, você foi o único com uma interpretação extra para lidar com 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/...
Cœur

11

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;
}

8

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.


3
@ Neil - como todas as respostas até agora mencionam o STL, suspeito que a pergunta foi editada rapidamente para remover uma referência desnecessária.
Michael Burr #

rand () não é inútil para números pequenos - você pode trocá-los por bits e usar apenas os bits mais aleatórios, se realmente precisar.
5309 Chris Lutz

@ Chris, você pode, se o tamanho do número aleatório for conhecido, mas se o tamanho necessário do número aleatório mudar durante o tempo de execução (como embaralhar uma matriz dinâmica etc.), seria difícil contornar essa advertência.
Dreamlax # 4/09

Não consigo encontrar qualquer função aleatório aqui :-(
kasia.b

@ kasia.b nesse link, existe extern int rand(void);e extern void srand(unsigned int);.
RastaJedi

7

Dê uma olhada no ISAAC (Indirection, Shift, Accumulate, Add e Count). É uniformemente distribuído e tem um ciclo médio de 2 ^ 8295.


2
O ISAAC é um RNG interessante devido à sua velocidade, mas ainda não recebeu atenção criptográfica séria.
precisa saber é o seguinte

4

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.


4

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.


6
Dois pontos a) seus números aleatórios não são "verdadeiramente" aleatórios, não importa como você semeia o gerador. Eb) é muito conveniente que a sequência pseudo-aleatória seja sempre a mesma em muitas circunstâncias - para testes, por exemplo.

16
se é MUITO IMPORTANTE que seu número seja verdadeiramente aleatório, você não deve usar a função rand ().
tylerl

1
Os valores de rand não são "verdadeiramente" aleatórios, não importa se você definir a semente ou não. Dada uma semente conhecida, a sequência é previsível. A geração de números aleatórios "verdadeiramente" é difícil. Não há entropia envolvida com a rand.
Dreamlax # /

2
Claro que sim - o gerador é semeado para você pela biblioteca (provavelmente a zero, mas é uma semente válida).

4
Ah, mas o algoritmo conhecido / a semente conhecida é essencial para depurar qualquer programa que use números aleatórios. Não é incomum registrar a semente usada junto com uma execução de simulação, para que possa ser recriada para análises mais detalhadas. Não chamar srand () é equivalente a chamar srand (1).
RBerteig

4

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.


4

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());
}

1
adicionar srand (rand ()) não aumenta a aleatoriedade da sequência se este programa for executado várias vezes em 1 segundo. time (NULL) ainda retornará o mesmo valor para cada um deles, o primeiro rand () retornará o mesmo longo e a segunda chamada para srand () terá o mesmo valor, resultando em ainda ter a mesma sequência aleatória. O uso do endereço argc pode ajudar, apenas se for garantido que esse endereço será diferente em todas as execuções do programa, o que nem sempre é verdadeiro.
precisa saber é o seguinte

3

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.


3

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)


@Pang é isso que eu claramente mencionados entre 9 e não 50 DE 9 e 50.
Shivam K. Thakkar

2
Sua operação do módulo apresentou um viés.
JWW

2

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.


1
Você também pode pegar um número aleatório de qualquer serviço online, como o random.org Bounty, se incluir uma maneira portátil e eficiente de fazer isso em C.
MD XF -

2
#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]);
}

em seguida, definir no topo com variáveis @ b4hand
Muhammad Sadiq

No momento em que fiz esse comentário, eu não tinha permissões universais de edição e, geralmente, as alterações de código que alteravam a resposta real seriam rejeitadas. Se você não quiser corrigir sua resposta, eu posso.
b4hand

Se sua intenção era produzir uma permutação aleatória de valores exclusivos, esse código ainda possui um erro. Ele produz o valor 84 duas vezes e não produz o valor 48. Além disso, ele não gera o gerador de números aleatórios, portanto a sequência é a mesma em todas as execuções.
b4hand

1
as variáveis ​​aeb são definidas neste código .. É livre de erros .. não há sintaxe e erro lógico também.
Muhammad Sadiq

Incorreta. Já confirmei manualmente a saída, como mencionei.
b4hand

1
#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;
}

1

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

1
#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;
}

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).


rand () pode falhar em outros testes de aleatoriedade, como os testes obstinados . rand () difere de plataforma para plataforma; Os valores rand () do GNU / Linux podem ser melhores que os valores do BSD ou do Windows.
George Koehler

Esta não é uma maneira válida de testar a aleatoriedade.
Veedrac

Depende do objetivo e do modelo de ameaça / risco. Para RNG criptograficamente forte, use RDRAND (ou RDSEED). Para um simples lançador de dados (não no nível do cassino) IMHO, o acima deve ser suficiente. A palavra-chave é "boa o suficiente ".
Mouse

0

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:

  • rand irá gerar a mesma sequência pseudo-aleatória, dar a mesma semente em srand (ver man srand);
  • Como já foi dito, a função de tempo muda apenas segundo a segundo: se seu aplicativo for executado várias vezes no mesmo segundo, 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:

  1. 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.

  2. 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);
  3. 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.


Mesmo com essas heurísticas, não confie em rand () para dados criptográficos.
domenukk 26/07/19

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.
26719 Koldar

0

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()e srand()na Biblioteca C do Linux usam o mesmo gerador de números aleatórios que random(3)e srandom(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 em random(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.
Björn Lindqvist

@ BjörnLindqvist O Windows também não é um sistema POSIX; é praticamente o único sistema no mercado que não suporta pelo menos as APIs POSIX básicas (que até bloqueiam sistemas como o iOS). O Windows é suportado apenas 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.
Mecki 13/01

0

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;
    }
}

-2

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));
} 

-3

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;
}

16
Este código não é bom. Ligar 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".
Blastfurnace 14/09

3
Esta função ficaria confusa com a random()função do Unix .
George Koehler
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.