Geração de números aleatórios em C ++ 11: como gerar, como funciona? [fechadas]

102

Recentemente, descobri uma nova maneira de gerar números aleatórios em C ++ 11, mas não consegui digerir os artigos que li sobre isso (o que é aquele mecanismo , termo matemático como distribuição , "onde todos os inteiros produzidos são igualmente prováveis ").

Então, alguém pode explicar

  • o que eles são?
  • o que eles significam?
  • como gerar?
  • como eles funcionam?
  • etc

Você pode chamar tudo em um FAQ sobre geração de números aleatórios.

informatik01
fonte
6
Perguntar sobre RNGs sem saber o que é uma distribuição é como perguntar sobre analisadores de expressão sem saber o que é uma expressão ... A biblioteca RNG em C ++ 11 é voltada para pessoas que conhecem algumas estatísticas e têm necessidades maiores do que a distribuição plana gerada por 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.
Matteo Italia
26
@Matteo: Dificilmente. Uma criança pode entender o conceito de que um dado produz números aleatórios, sem entender o que é uma distribuição.
Benjamin Lindley
3
@Benjamin: e é aí que pára o seu entendimento, que é só o primeiro passo (os motores), e mesmo sem entender porque é importante que eles gerem uma distribuição plana. Todo o resto da biblioteca permanece um mistério sem a compreensão de distribuições e outros conceitos de estatística.
Matteo Italia

Respostas:

142

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::mt19937a uint32_tem x64, eventualmente, isso deve ser fixo e você pode dizer MyRNG::result_type seed_vale, assim, fazer o motor muito facilmente substituível.
Kerrek SB
fonte
Mais uma vez, Kerrek me bateu com uma resposta muito melhor do que aquela em que eu estava trabalhando. +1
Justin ᚅᚔᚈᚄᚒᚔ
@ Justin: Tenho certeza de que perdi muitas coisas, fique à vontade para acrescentar mais aspectos a este tópico! :-)
Kerrek SB
13
Para a parte "povoar de alguma forma", acho que std::random_devicevale a pena mencionar em vez de/dev/urandom
Cubbi
2
Um exemplo de std::random_devicepode ser encontrado aqui .
WKS
1
O código do artigo da Wikipedia está cheio de erros. random e random2 são idênticos. A partir dos comentários no trecho de código, fica claro que o autor não entende como usar os recursos em <random>.
user515430
3

Um gerador de números aleatórios é uma equação que, dado um número, dará a você um novo número. Normalmente, você fornece o primeiro número ou é obtido de algo como a hora do sistema.

Cada vez que você pede um novo número, ele usa o número anterior para executar a equação.

Um gerador de números aleatórios não é considerado muito bom se tiver a tendência de produzir o mesmo número com mais freqüência do que outros números. ou seja, se você quisesse um número aleatório entre um e 5 e tivesse esta distribuição de números:

  • 1: 1%
  • 2: 80%
  • 3: 5%
  • 4: 5%
  • 5: 9%

2 é gerado com muito mais frequência do que qualquer outro número, portanto, é mais provável que seja produzido do que outros números. Se todos os números fossem iguais, você teria 20% de chance de obter cada número todas as vezes. Dito de outra forma, a distribuição acima é muito desigual porque 2 é o preferido. Uma distribuição com todos os 20% seria uniforme.

Normalmente, se você quiser um número aleatório verdadeiro, deverá obter dados de algo como o clima ou alguma outra fonte natural, em vez de um gerador de números aleatórios.

N / D
fonte
8
A maioria dos geradores de números aleatórios gera boas distribuições uniformes. Eles simplesmente não são aleatórios; o problema é que eles são calculados e, portanto, você pode adivinhar o próximo número dado um número suficiente na sequência (esse fato os torna ruins para a segurança, onde números realmente aleatórios são necessários). Para jogos e outras coisas, você deve estar bem.
Martin York
5
Tenho certeza que o OP está pedindo informações específicas sobre os recursos fornecidos no cabeçalho C ++ <random>. Essa resposta nem mesmo aborda a programação, muito menos C ++.
Benjamin Lindley
1
@Martin: A segurança não requer necessariamente uma fonte de números verdadeiramente aleatórios. O AES no modo contador (por exemplo) pode funcionar muito bem, embora seja determinístico. Requer uma quantidade razoável de entropia na chave, mas não qualquer aleatoriedade verdadeira.
Jerry Coffin de
@Benjamin Lindley: Deixa pra lá. Apenas reli e percebi que estava errado.
N_A de