Esta é uma continuação de uma pergunta postada anteriormente:
Como gerar um número aleatório em C?
Desejo ser capaz de gerar um número aleatório dentro de um determinado intervalo, como 1 a 6, para imitar os lados de um dado.
Como eu faria isso?
Esta é uma continuação de uma pergunta postada anteriormente:
Como gerar um número aleatório em C?
Desejo ser capaz de gerar um número aleatório dentro de um determinado intervalo, como 1 a 6, para imitar os lados de um dado.
Como eu faria isso?
Respostas:
Todas as respostas até agora estão matematicamente erradas. Retornar
rand() % N
não fornece uniformemente um número no intervalo, a[0, N)
menos queN
divida a duração do intervalo no qualrand()
retorna (ou seja, é uma potência de 2). Além disso, não se tem idéia se os módulos derand()
são independentes: é possível que eles vão0, 1, 2, ...
, o que é uniforme, mas não muito aleatório. A única suposição que parece razoável fazer é que produzrand()
uma distribuição de Poisson: quaisquer dois subintervalos não sobrepostos do mesmo tamanho são igualmente prováveis e independentes. Para um conjunto finito de valores, isso implica uma distribuição uniforme e também garante que os valores derand()
sejam bem dispersos.Isso significa que a única maneira correta de alterar o intervalo de
rand()
é dividi-lo em caixas; por exemplo, seRAND_MAX == 11
você quiser um intervalo de1..6
, deve atribuir{0,1}
a 1,{2,3}
a 2 e assim por diante. Esses são intervalos separados e de tamanhos iguais e, portanto, são uniformemente e independentemente distribuídos.A sugestão de usar a divisão de ponto flutuante é matematicamente plausível, mas apresenta problemas de arredondamento em princípio. Talvez
double
seja uma precisão alta o suficiente para fazê-lo funcionar; talvez não. Eu não sei e não quero ter que descobrir; em qualquer caso, a resposta depende do sistema.A maneira correta é usar aritmética inteira. Ou seja, você deseja algo como o seguinte:
O loop é necessário para obter uma distribuição perfeitamente uniforme. Por exemplo, se você receber números aleatórios de 0 a 2 e quiser apenas números de 0 a 1, continue puxando até não obter um 2; não é difícil verificar se isso dá 0 ou 1 com probabilidade igual. Esse método também é descrito no link que ns forneceu na resposta, embora codificado de forma diferente. Estou usando em
random()
vez derand()
porque tem uma distribuição melhor (conforme observado na página do manual pararand()
).Se você quiser obter valores aleatórios fora da faixa padrão
[0, RAND_MAX]
, terá que fazer algo complicado. Talvez o mais expediente seja definir uma funçãorandom_extended()
que extraian
bits (usandorandom_at_most()
) e retorna[0, 2**n)
, e então aplicarrandom_at_most()
comrandom_extended()
no lugar derandom()
(e2**n - 1
no lugar deRAND_MAX
) para extrair um valor aleatório menor que2**n
, supondo que você tenha um tipo numérico que pode conter tal um valor. Finalmente, é claro, você pode obter valores em[min, max]
usomin + random_at_most(max - min)
, incluindo valores negativos.fonte
max - min > RAND_MAX
, o que é mais sério do que o problema que afirmei acima (por exemplo, o VC ++ temRAND_MAX
de apenas 32.767).do {} while()
.Seguindo a resposta de @Ryan Reich, pensei em oferecer minha versão limpa. A primeira verificação de limites não é necessária devido à segunda verificação de limites, e a tornei iterativa em vez de recursiva. Ele retorna valores no intervalo [min, max], onde
max >= min
e1+max-min < RAND_MAX
.fonte
limit
um int (e opcionalmentebucket
também) desdeRAND_MAX / range
<INT_MAX
ebuckets * range
<=RAND_MAX
. EDITAR: Enviei e editei a proposta.Esta é uma fórmula se você souber os valores máximos e mínimos de um intervalo e quiser gerar números inclusivos entre o intervalo:
fonte
int
transbordamento commax+1-min
.Veja aqui outras opções.
fonte
(((max-min+1)*rand())/RAND_MAX)+min
e obter provavelmente a mesma distribuição exata (assumindo que RAND_MAX é pequeno o suficiente em relação ao int para não estourar).max + 1
, se umrand() == RAND_MAX
ou outrorand()
estiver muito próximoRAND_MAX
e erros de ponto flutuante ultrapassem o resultado finalmax + 1
. Por segurança, você deve verificar se o resultado está dentro da faixa antes de retorná-lo.RAND_MAX + 1.0
. Ainda não tenho certeza se isso é bom o suficiente para evitar ummax + 1
retorno, no entanto: em particular, o+ min
no final envolve uma rodada que pode acabar produzindomax + 1
grandes valores de rand (). Mais seguro abandonar totalmente essa abordagem e usar a aritmética de inteiros.RAND_MAX
é substituída porRAND_MAX+1.0
como Christoph sugere, então eu acredito que este é seguro, desde que o+ min
é feito usando inteiro aritmética:return (unsigned int)((max - min + 1) * scaled) + min
. A razão (não óbvia) é que assumindo IEEE 754 aritmética e arredondamento meio para par, (e também issomax - min + 1
é exatamente representável como um duplo, mas isso será verdade em uma máquina típica), é sempre verdade quex * scaled < x
para qualquer duplo positivox
e qualquer duploscaled
satisfatório0.0 <= scaled && scaled < 1.0
.randr(0, UINT_MAX)
: sempre gera 0.Você não faria apenas:
%
é o operador de módulo. Essencialmente, ele vai apenas dividir por 6 e retornar o restante ... de 0 - 5fonte
rand()
inclua os bits de ordem inferior do estado do gerador (se ele usar um LCG). Eu não vi um até agora - todos eles (sim, incluindo MSVC com RAND_MAX sendo apenas 32767) removem os bits de ordem inferior. O uso de módulo não é recomendado por outras razões, nomeadamente porque distorce a distribuição a favor de números menores.Para aqueles que entendem o problema de polarização, mas não suportam o tempo de execução imprevisível de métodos baseados em rejeição, esta série produz um número inteiro aleatório progressivamente menos polarizado no
[0, n-1]
intervalo:Ele faz isso sintetizando um número aleatório de
i * log_2(RAND_MAX + 1)
bits de ponto fixo de alta precisão (ondei
é o número de iterações) e realizando uma longa multiplicação porn
.Quando o número de bits é suficientemente grande em comparação com
n
, a tendência torna-se incomensuravelmente pequena.Não importa se
RAND_MAX + 1
é menor quen
(como nesta questão ), ou se não é uma potência de dois, mas deve-se tomar cuidado para evitar estouro de inteiro seRAND_MAX * n
for grande.fonte
RAND_MAX
é frequentementeINT_MAX
, entãoRAND_MAX + 1
-> UB (como INT_MIN)RAND_MAX * n
for grande". Você precisa organizar o uso de tipos apropriados para suas necessidades.RAND_MAX
geralmente éINT_MAX
" Sim, mas apenas em sistemas de 16 bits! Qualquer arquitetura razoavelmente moderna será colocadaINT_MAX
em 2 ^ 32/2 eRAND_MAX
em 2 ^ 16 / 2. Esta é uma suposição incorreta?int
compiladores de 32 bits , encontreiRAND_MAX == 32767
em um eRAND_MAX == 2147483647
em outro. Minha experiência geral (décadas) é isso comRAND_MAX == INT_MAX
mais frequência. Portanto, discorde que uma arquitetura razoavelmente moderna de 32 bits certamente terá umRAND_MAX
at2^16 / 2
. Já que a especificação C permite32767 <= RAND_MAX <= INT_MAX
, eu codifico para isso de qualquer maneira, e não uma tendência.Para evitar o viés do módulo (sugerido em outras respostas), você sempre pode usar:
Onde "MAX" é o limite superior e "MIN" é o limite inferior. Por exemplo, para números entre 10 e 20:
Solução simples e melhor do que usar "rand ()% N".
fonte
#include <bsd/stdlib.h>
primeiro. Além disso, alguma ideia de como fazer isso no Windows sem MinGW ou CygWin?Aqui está um algoritmo ligeiramente mais simples do que a solução de Ryan Reich:
fonte
RAND_MAX + 1
pode facilmente transbordarint
adição. Nesse caso,(RAND_MAX + 1) % range
gerará resultados questionáveis. Considere(RAND_MAX + (uint32_t)1)
Embora Ryan esteja correto, a solução pode ser muito mais simples com base no que se sabe sobre a origem da aleatoriedade. Para reafirmar o problema:
[0, MAX)
com distribuição uniforme.[rmin, rmax]
onde0 <= rmin < rmax < MAX
.Na minha experiência, se o número de caixas (ou "caixas") for significativamente menor do que o intervalo dos números originais, e a fonte original for criptograficamente forte - não há necessidade de passar por todo aquele rigamarole, e a divisão simples do módulo faria são suficientes (como
output = rnd.next() % (rmax+1)
, sermin == 0
) e produzem números aleatórios que são distribuídos uniformemente "o suficiente" e sem qualquer perda de velocidade. O fator chave é a fonte de aleatoriedade (ou seja, crianças, não tente fazer isso em casa comrand()
).Aqui está um exemplo / prova de como funciona na prática. Eu queria gerar números aleatórios de 1 a 22, tendo uma fonte criptograficamente forte que produzisse bytes aleatórios (com base em Intel RDRAND). Os resultados são:
Isso é o mais uniforme que preciso para o meu propósito (lançamento de dados justo, geração de livros de código criptograficamente fortes para máquinas de criptografia da Segunda Guerra Mundial, como http://users.telenet.be/d.rijmenants/en/kl-7sim.htm , etc. ) A saída não mostra qualquer tendência apreciável.
Aqui está a fonte do gerador de números aleatórios criptograficamente forte (verdadeiro): Intel Digital Random Number Generator e um código de amostra que produz números aleatórios de 64 bits (sem sinal).
Compilei-o no Mac OS X com clang-6.0.1 (direto) e com gcc-4.8.3 usando o sinalizador "-Wa, q" (porque o GAS não suporta essas novas instruções).
fonte
gcc randu.c -o randu -Wa,q
(GCC 5.3.1 no Ubuntu 16) ouclang randu.c -o randu
(Clang 3.8.0) funciona, mas descarta o núcleo em tempo de execução comIllegal instruction (core dumped)
. Alguma ideia?rand()
. Tentei alguns testes e postei essa pergunta, mas ainda não consigo encontrar uma resposta definitiva.Como dito antes, o módulo não é suficiente porque distorce a distribuição. Aqui está o meu código que mascara os bits e os usa para garantir que a distribuição não seja distorcida.
O código simples a seguir permite que você observe a distribuição:
fonte
v = rand(); if (v > RAND_MAX - (RAND_MAX % range) -> reject and try again; else return v % range;
Eu entendo que o módulo é uma operação muito mais lenta do que o mascaramento, mas ainda acho ... que deve ser testado.rand()
retorna umint
no intervalo[0..RAND_MAX]
. Esse intervalo pode facilmente ser um subintervalo deuint32_t
erandomInRange(0, ,b)
nunca gera valores no intervalo(INT_MAX...b]
.Retornará um número de ponto flutuante no intervalo [0,1]:
fonte