Os números aleatórios do C vêm de duas funções em <stdlib.h>: rand(), que produz o próximo valor, e srand(), que define o ponto de partida. Eles não são verdadeiramente aleatórios - são uma sequência pseudoaleatória, calculada deterministicamente a partir de uma semente - o que é uma limitação para criptografia e um recurso para testes.
rand() e RAND_MAX
rand() devolve um int em algum lugar entre 0 e RAND_MAX, inclusive. RAND_MAX é uma macro garantidamente de pelo menos 32767; no Linux e no macOS ela é 2147483647.
Execute isso duas vezes. Os números são idênticos nas duas - e isso não é um bug.
Semeando com srand
Sem uma chamada a srand, a sequência se comporta como se você tivesse chamado srand(1). Mesma semente, mesma sequência, toda execução. Para obter números diferentes a cada execução, semeie com algo que muda - convencionalmente, a hora atual:
time(NULL), de <time.h>, devolve os segundos desde o início de 1970, então cada execução recebe uma semente diferente. A conversão para unsigned int silencia um aviso sobre estreitar o time_t.
Três regras sobre semear, todas as quais as pessoas erram:
Semeie exatamente uma vez, no começo de main. Chamar srand antes de cada rand() é o antipadrão clássico - dentro de um laço que termina em menos de um segundo, time(NULL) devolve o mesmo valor a cada iteração, então você resemeia com o mesmo número e rand() devolve o mesmo primeiro valor toda vez. A saída é uma coluna de números "aleatórios" idênticos.
Não resemeie para "melhorar" a aleatoriedade. A qualidade do gerador vem do avanço do seu estado interno; reiniciar esse estado joga a sequência fora.
time(NULL) tem resolução de um segundo. Dois programas lançados no mesmo segundo recebem a mesma sequência. Isso serve para um jogo e é errado para qualquer coisa em que a independência importa.
Um número em um intervalo
O idioma padrão usa o operador de resto:
rand() % n /* 0 ate n-1 */
rand() % n + min /* min ate min+n-1 */
Para obter de min até max inclusive, a quantidade de valores possíveis é max - min + 1:
O + 1 é onde moram os erros de deslocamento por um. rand() % 6 dá de 0 a 5, então um lançamento de dado é rand() % 6 + 1. Escrever rand() % 7 + 1 para "incluir o 6" te dá um dado de sete faces.
A nota honesta sobre o viés do módulo
rand() % n não é perfeitamente uniforme a menos que n divida RAND_MAX + 1 exatamente.
Pense com números pequenos. Se RAND_MAX fosse 9 - então rand() devolve de 0 a 9, dez valores igualmente prováveis - rand() % 3 mapeia 0,3,6,9 para 0; 1,4,7 para 1; e 2,5,8 para 2. O resultado 0 ocorre de quatro maneiras em dez, e os resultados 1 e 2 de três maneiras cada. O zero é 33% mais provável.
O mesmo desvio existe com o RAND_MAX real, só que bem menor: os valores sobrando são os primeiros (RAND_MAX + 1) % n resultados, cada um ganhando uma chance extra em cerca de 2,1 bilhões. Para um lançamento de dado, um baralho embaralhado ou uma simulação, isso é imensurável - use % e siga em frente.
Quando isso importa - trabalho estatístico, qualquer coisa ligada a segurança - rejeite os valores sobrando em vez de dobrá-los para dentro:
O laço joga fora a pequena faixa de valores que causaria o desvio e sorteia de novo. Ele termina rápido - a fatia rejeitada é uma fração ínfima do todo.
Para aleatoriedade genuinamente sensível a segurança, rand() é a ferramenta errada por mais cuidado que se tome: use arc4random_buf no macOS e no BSD, getrandom() no Linux, ou BCryptGenRandom no Windows.
Doubles aleatórios
Divida por RAND_MAX para cair em [0.0, 1.0] e depois escale:
A conversão em (double) rand() é essencial. Sem ela, rand() / RAND_MAX é divisão inteira e resulta em 0 quase sempre, e 1 na chance de um em dois bilhões de acertar o máximo - um bug que parece "meus doubles aleatórios são todos zero". Veja conversão de tipos para entender por quê.
Sequências reprodutíveis
Uma semente fixa dá a sequência idêntica em toda execução, que é exatamente o que você quer para um teste, uma sessão de depuração ou um jogo com códigos de fase compartilháveis:
A semente 42 produz os mesmos cinco números toda vez que é usada, nesta execução e em qualquer outra com a mesma biblioteca. Essa reprodutibilidade é por que uma simulação deveria permitir escolher a semente: rode com o relógio normalmente e passe uma semente fixa quando for reproduzir um bug.
Uma ressalva: a sequência de uma dada semente não é portável. Bibliotecas C diferentes usam geradores diferentes, então a semente 42 na glibc e a semente 42 no Windows dão números diferentes. Reprodutível em uma máquina, não entre máquinas.
Um jogo de dados
Tudo junto - semear uma vez, uma função auxiliar para o intervalo e um array contabilizando os resultados:
O histograma deve ter pico em 7 e afinar em direção a 2 e 12 - há seis maneiras de fazer 7 e uma maneira de fazer 2 ou 12. Um gerador que produzisse uma distribuição plana aqui estaria quebrado.
Duas páginas relacionadas: a biblioteca padrão mapeia o resto de <stdlib.h>, e funções matemáticas cobre <math.h>, que você vai querer assim que valores aleatórios alimentarem cálculos de verdade.
Perguntas frequentes
Como gerar um número aleatório em C?
Inclua <stdlib.h>, semeie uma vez no começo de main com srand((unsigned) time(NULL)) (que precisa de <time.h>) e então chame rand() para cada valor. rand() devolve um int entre 0 e RAND_MAX, inclusive.
Como obter um número aleatório entre dois valores em C?
Use rand() % (max - min + 1) + min. Para um lançamento de dado entre 1 e 6 isso é rand() % 6 + 1. O % n mapeia o resultado em 0..n-1 e somar min desloca a janela - só garanta que a contagem inclua os dois extremos, que é o que o + 1 faz.
Por que meu programa em C imprime os mesmos números aleatórios toda vez?
Porque você nunca chamou srand. Sem uma semente, rand() se comporta como se tivesse sido semeado com 1, então toda execução produz a sequência idêntica. Chame srand((unsigned) time(NULL)) uma vez no início do programa - uma vez, não antes de cada chamada de rand(), o que pioraria as coisas.
O que é viés do módulo na geração de números aleatórios?
rand() % n só é perfeitamente uniforme quando n divide RAND_MAX + 1 exatamente. Do contrário, os primeiros valores ocorrem uma vez a mais ao longo de toda a faixa, ficando levemente mais prováveis. Com RAND_MAX em 2147483647 e um n pequeno, o desvio fica muito abaixo do que um jogo ou uma simulação percebe, mas para criptografia ou estatística use um laço de rejeição ou um gerador apropriado.