A pergunta é muito ampla para uma resposta completa, mas deixe-me escolher alguns pontos interessantes:
Por que "igualmente provável"
Suponha que você tenha um gerador de números aleatórios simples que gere os números 0, 1, ..., 10 cada com probabilidade igual (pense nisso como um clássico rand()
). Agora você quer um número aleatório no intervalo 0, 1, 2, cada um com probabilidade igual. Sua reação automática seria aceitar rand() % 3
. Mas espere, os restos 0 e 1 ocorrem com mais freqüência do que os restantes 2, então isso não está correto!
É por isso que precisamos de distribuições adequadas , que pegam uma fonte de inteiros aleatórios uniformes e os transformam em nossa distribuição desejada, como Uniform[0,2]
no exemplo. Melhor deixar isso para uma boa biblioteca!
Motores
Assim, no cerne de toda aleatoriedade está um bom gerador de números pseudo-aleatórios que gera uma sequência de números que se distribui uniformemente por um certo intervalo e que, idealmente, têm um período muito longo. A implementação padrão do rand()
nem sempre é a melhor e, portanto, é bom ter uma escolha. Linear-congruential e o twister Mersenne são duas boas escolhas (LG também é frequentemente usado por rand()
); novamente, é bom deixar a biblioteca cuidar disso.
Como funciona
Fácil: primeiro, configure um motor e semeie-o. A semente determina totalmente a sequência inteira de números "aleatórios", então a) use um diferente (por exemplo, tirado de /dev/urandom
) a cada vez, eb) armazene a semente se desejar recriar uma sequência de escolhas aleatórias.
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
Agora podemos criar distribuições:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
... E use o motor para criar números aleatórios!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Simultaneidade
Mais uma razão importante para preferir <random>
o tradicional rand()
é que agora é muito claro e óbvio como tornar a geração de números aleatórios segura para thread: fornecer a cada thread seu próprio mecanismo local de thread, propagado em uma semente local de thread ou sincronizar o acesso para o objeto do motor.
Misc
- Um artigo interessante sobre TR1 random no codeguru.
- A Wikipedia tem um bom resumo (obrigado, @Justin).
- Em princípio, cada motor deve typedef a
result_type
, que é o tipo integral correto a ser usado para a semente. Eu acho que tinha uma implementação de buggy, uma vez que me obrigou a forçar a semente para std::mt19937
a uint32_t
em x64, eventualmente, isso deve ser fixo e você pode dizer MyRNG::result_type seed_val
e, assim, fazer o motor muito facilmente substituível.
rand
, você deve dar uma olhada rápida na Wikipedia para alguns conceitos básicos de estatística e RNG, caso contrário, será realmente difícil explicar a razão<random>
e o uso de suas várias partes.