Eu preciso de uma função que gere um número inteiro aleatório em determinado intervalo (incluindo valores de borda). Não tenho requisitos de qualidade / aleatoriedade irracionais, tenho quatro requisitos:
- Eu preciso que seja rápido. Meu projeto precisa gerar milhões (ou às vezes dezenas de milhões) de números aleatórios e minha função atual de gerador provou ser um gargalo.
- Eu preciso que ele seja razoavelmente uniforme (o uso de rand () é perfeitamente adequado).
- os intervalos min-max podem ser de <0, 1> a <-32727, 32727>.
- tem que ser semeada.
Atualmente, tenho o seguinte código C ++:
output = min + (rand() * (int)(max - min) / RAND_MAX)
O problema é que ele não é realmente uniforme - max é retornado apenas quando rand () = RAND_MAX (para Visual C ++ é 1/32727). Esse é um problema importante para pequenos intervalos como <-1, 1>, onde o último valor quase nunca é retornado.
Peguei caneta e papel e criei a seguinte fórmula (que se baseia no truque de arredondamento inteiro (int) (n + 0,5)):
Mas ainda não me dá uma distribuição uniforme. Execuções repetidas com 10000 amostras fornecem uma proporção de 37:50:13 para valores de valores -1, 0. 1.
Você poderia sugerir uma fórmula melhor? (ou até mesmo a função gerador de números pseudo-aleatórios)
Respostas:
Uma solução distribuída rápida, um pouco melhor que a sua, mas ainda não adequadamente uniforme, é
Exceto quando o tamanho do intervalo é uma potência de 2, esse método produz números distribuídos não uniformes tendenciosos, independentemente da qualidade de
rand()
. Para um teste abrangente da qualidade deste método, leia isto .fonte
rand()
deve ser considerado nocivo em C ++, existem maneiras muito melhores de obter algo distribuído uniformemente e realmente aleatório.A resposta mais simples (e, portanto, a melhor) em C ++ (usando o padrão de 2011) é
Não há necessidade de reinventar a roda. Não precisa se preocupar com preconceitos. Não precisa se preocupar em usar o tempo como semente aleatória.
fonte
random_device
, o que pode ser completamente quebrado em alguns casos . Além disso,mt19937
apesar de ser uma escolha de uso geral muito boa, não é o mais rápido dos geradores de boa qualidade (veja esta comparação ) e, portanto, pode não ser o candidato ideal para o OP.minstd
seja esse método), mas isso é progresso. Quanto à má implementação dorandom_device
- isso é horrível e deve ser considerado um bug (possivelmente também do padrão C ++, se permitir).rand()
não é uma opção e isso importa para o uso não crítico, como gerar um índice dinâmico aleatório? Além disso, eu tenho que me preocupar em construirrandom_device
/mt19937
/uniform_int_distribution
em um loop apertado / função embutida? Eu prefiro preferir passá-los por aí?Se o seu compilador suportar C ++ 0x e usá-lo for uma opção para você,
<random>
é provável que o novo cabeçalho padrão atenda às suas necessidades. Possui alta qualidade,uniform_int_distribution
que aceita limites mínimos e máximos (inclusive conforme necessário), e você pode escolher entre vários geradores de números aleatórios para conectar-se a essa distribuição.Aqui está o código que gera um milhão de aleatórios
int
distribuídos uniformemente em [-57, 365]. Usei as novas<chrono>
instalações std para cronometrar, pois você mencionou que o desempenho é uma grande preocupação para você.Para mim (Intel Core i5 de 2,8 GHz), é impresso:
2.10268e + 07 números aleatórios por segundo.
Você pode propagar o gerador passando um int para seu construtor:
Se, posteriormente, você descobrir que
int
não cobre o intervalo necessário para sua distribuição, isso pode ser remediado alterando-se o seguinteuniform_int_distribution
(por exemplo, paralong long
):Se você descobrir mais tarde que o
minstd_rand
gerador não é de alta qualidade, isso também pode ser facilmente trocado. Por exemplo:Ter controle separado sobre o gerador de números aleatórios e a distribuição aleatória pode ser bastante libertadora.
Também calculei (não mostrado) os 4 primeiros "momentos" desta distribuição (usando
minstd_rand
) e os comparei aos valores teóricos na tentativa de quantificar a qualidade da distribuição:(O
x_
prefixo refere-se a "esperado")fonte
d
a cada iteração com limites diferentes? Quanto isso desaceleraria o ciclo?Vamos dividir o problema em duas partes:
n
no intervalo de 0 a (max-min).A primeira parte é obviamente a mais difícil. Vamos supor que o valor de retorno de rand () seja perfeitamente uniforme. Usar o módulo adicionará um viés aos primeiros
(RAND_MAX + 1) % (max-min+1)
números. Portanto, se pudéssemos mudar magicamenteRAND_MAX
paraRAND_MAX - (RAND_MAX + 1) % (max-min+1)
, não haveria mais preconceito.Acontece que podemos usar essa intuição se estivermos dispostos a permitir o pseudo-não-determinismo no tempo de execução do nosso algoritmo. Sempre que rand () retorna um número muito grande, simplesmente pedimos outro número aleatório até obtermos um número suficientemente pequeno.
O tempo de execução agora está distribuído geometricamente , com valor esperado,
1/p
ondep
está a probabilidade de obter um número pequeno o suficiente na primeira tentativa. ComoRAND_MAX - (RAND_MAX + 1) % (max-min+1)
sempre é menor que(RAND_MAX + 1) / 2
, sabemos quep > 1/2
, portanto, o número esperado de iterações sempre será menor que duas para qualquer intervalo. Deve ser possível gerar dezenas de milhões de números aleatórios em menos de um segundo em uma CPU padrão com esta técnica.EDITAR:
Embora o acima exposto seja tecnicamente correto, a resposta do DSimon é provavelmente mais útil na prática. Você não deve implementar essas coisas sozinho. Eu já vi muitas implementações de amostragem por rejeição e geralmente é muito difícil ver se está correto ou não.
fonte
Que tal o Mersenne Twister ? A implementação do impulso é bastante fácil de usar e é bem testada em muitos aplicativos do mundo real. Eu mesmo o usei em vários projetos acadêmicos, como inteligência artificial e algoritmos evolutivos.
Aqui está o exemplo deles, onde eles fazem uma função simples para rolar um dado de seis lados:
Ah, e aqui está um pouco mais deste cafetão, caso você não esteja convencido de que deve usá-lo sobre o amplamente inferior
rand()
:fonte
boost::uniform_int
distribuição), você pode transformar os intervalos mín. Máx. Em qualquer coisa que desejar e é fácil de cultivar.Este é um mapeamento de 32768 inteiros para (nMax-nMin + 1) inteiros. O mapeamento será bastante bom se (nMax-nMin + 1) for pequeno (como no seu requisito). Observe, porém, que se (nMax-nMin + 1) for grande, o mapeamento não funcionará (por exemplo - você não pode mapear valores 32768 para 30000 valores com probabilidade igual). Se esses intervalos forem necessários - você deve usar uma fonte aleatória de 32 ou 64 bits, em vez dos rand de 15 bits () ou ignorar os resultados de rand () que estão fora do intervalo.
fonte
RAND_MAX
para(double) RAND_MAX
para evitar um aviso de estouro inteiro.Aqui está uma versão imparcial que gera números em
[low, high]
:Se seu intervalo for razoavelmente pequeno, não há motivo para armazenar em cache o lado direito da comparação no
do
loop.fonte
[0, h)
simples. A chamadarand()
temRAND_MAX + 1
possíveis valores de retorno; levandorand() % h
recolhimentos(RAND_MAX + 1) / h
deles para cada um dosh
valores de saída, exceto que(RAND_MAX + 1) / h + 1
eles são mapeados para valores inferiores a(RAND_MAX + 1) % h
(por causa do último ciclo parcial através dash
saídas). Portanto, removemos os(RAND_MAX + 1) % h
possíveis resultados para obter uma distribuição imparcial.Eu recomendo a biblioteca Boost.Random , é super detalhada e bem documentada, permite especificar explicitamente qual distribuição você deseja e, em cenários não criptográficos, pode realmente superar a implementação típica de uma biblioteca C.
fonte
suponha que min e max são valores int, [e] significa incluir esse valor, (e) significa não incluir esse valor, usando acima para obter o valor correto usando c ++ rand ()
referência: para () [] definir, visite:
https://en.wikipedia.org/wiki/Interval_(mathematics)
para a função rand e srand ou RAND_MAX define, visite:
http://en.cppreference.com/w/cpp/numeric/random/rand
[mínimo máximo]
(mínimo máximo]
[mínimo máximo)
(mínimo máximo)
fonte
Neste tópico, a amostragem de rejeição já foi discutida, mas eu queria sugerir uma otimização com base no fato de que
rand() % 2^something
não introduz viés, como já mencionado acima.O algoritmo é realmente simples:
Aqui está o meu código de exemplo:
Isso funciona bem especialmente para pequenos intervalos, porque a potência de 2 será "mais próxima" da duração real do intervalo e, portanto, o número de erros será menor.
PS
Obviamente, evitar a recursão seria mais eficiente (não é necessário calcular repetidamente o teto do log ..), mas achei que era mais legível para este exemplo.
fonte
Observe que, na maioria das sugestões, o valor aleatório inicial obtido da função rand (), que normalmente é de 0 a RAND_MAX, é simplesmente desperdiçado. Você está criando apenas um número aleatório, enquanto existe um procedimento sólido que pode lhe dar mais.
Suponha que você deseja a região [min, max] de números aleatórios inteiros. Começamos de [0, max-min]
Pegue a base b = max-min + 1
Comece representando um número obtido de rand () na base b.
Dessa forma, você obtém o piso (log (b, RAND_MAX)) porque cada dígito na base b, exceto possivelmente o último, representa um número aleatório no intervalo [0, max-min].
É claro que o deslocamento final para [min, max] é simples para cada número aleatório r + min.
Se NUM_DIGIT é o número de dígitos na base b que você pode extrair e que é
o exposto acima é uma implementação simples de extração de números aleatórios NUM_DIGIT de 0 a b-1 de um número aleatório RAND_MAX que fornece b <RAND_MAX.
fonte
A fórmula para isso é muito simples, então tente esta expressão,
fonte
int num = (int) rand() % (max - min) + min;
A seguinte expressão deve ser imparcial se não me engano:
Estou assumindo aqui que rand () fornece um valor aleatório no intervalo entre 0,0 e 1,0, NÃO incluindo 1,0 e que max e min são números inteiros com a condição que min <max.
fonte
std::floor
retornadouble
e precisamos de um valor inteiro aqui. Gostaria apenas de transmitir, emint
vez de usarstd::floor
.