Como posso gerar facilmente números aleatórios seguindo uma distribuição normal em C ou C ++?
Não quero usar Boost.
Sei que Knuth fala longamente sobre isso, mas não tenho seus livros em mãos agora.
c++
c
random
distribution
normal-distribution
Damien
fonte
fonte
Respostas:
Existem muitos métodos para gerar números com distribuição gaussiana a partir de um RNG regular .
A transformação Box-Muller é comumente usada. Ele produz valores corretamente com uma distribuição normal. A matemática é fácil. Você gera dois números aleatórios (uniformes) e, ao aplicar uma fórmula a eles, obtém dois números aleatórios normalmente distribuídos. Retorne um e salve o outro para a próxima solicitação de um número aleatório.
fonte
std::normal_distribution
que faz exatamente o que você pede sem se aprofundar em detalhes matemáticos.C ++ 11
C ++ 11 oferece
std::normal_distribution
, que é o caminho que eu faria hoje.C ou C ++ mais antigo
Aqui estão algumas soluções em ordem de complexidade crescente:
Adicione 12 números aleatórios uniformes de 0 a 1 e subtraia 6. Isso corresponderá à média e ao desvio padrão de uma variável normal. Uma desvantagem óbvia é que o intervalo é limitado a ± 6 - ao contrário de uma distribuição normal verdadeira.
A transformação Box-Muller. Isso está listado acima e é relativamente simples de implementar. Se você precisar de amostras muito precisas, no entanto, esteja ciente de que a transformada Box-Muller combinada com alguns geradores uniformes sofre de uma anomalia chamada Efeito Neave 1 .
Para melhor precisão, sugiro desenhar uniformes e aplicar a distribuição normal cumulativa inversa para chegar às variáveis normalmente distribuídas. Aqui está um algoritmo muito bom para distribuições normais cumulativas inversas.
1. HR Neave, "Sobre o uso da transformação de Box-Muller com geradores de números pseudo-aleatórios congruenciais multiplicativos", Applied Statistics, 22, 92-97, 1973
fonte
Um método rápido e fácil é somar um número de números aleatórios distribuídos uniformemente e obter sua média. Veja o Teorema do Limite Central para uma explicação completa de por que isso funciona.
fonte
Eu criei um projeto de código aberto C ++ para benchmark de geração de número aleatório normalmente distribuído .
Ele compara vários algoritmos, incluindo
cpp11random
usa C ++ 11std::normal_distribution
comstd::minstd_rand
(na verdade é uma transformação Box-Muller em clang).Os resultados da
float
versão de precisão única ( ) no iMac [email protected], clang 6.1, 64 bits:Para correção, o programa verifica a média, desvio padrão, assimetria e curtose das amostras. Verificou-se que o método CLT pela soma de 4, 8 ou 16 números uniformes não apresenta boa curtose como os demais métodos.
O algoritmo Zigurate tem melhor desempenho que os outros. No entanto, ele não é adequado para paralelismo SIMD, pois precisa de consulta de tabela e ramificações. Box-Muller com conjunto de instruções SSE2 / AVX é muito mais rápido (x1,79, x2,99) do que a versão não SIMD do algoritmo zigurate.
Portanto, vou sugerir o uso de Box-Muller para arquitetura com conjuntos de instruções SIMD, e pode zigurar caso contrário.
PS o benchmark usa um LCG PRNG mais simples para gerar números aleatórios distribuídos uniformes. Portanto, pode não ser suficiente para alguns aplicativos. Mas a comparação de desempenho deve ser justa porque todas as implementações usam o mesmo PRNG, portanto, o benchmark testa principalmente o desempenho da transformação.
fonte
Aqui está um exemplo de C ++, baseado em algumas das referências. Isso é rápido e sujo, é melhor você não reinventar e usar a biblioteca boost.
Você pode usar um gráfico QQ para examinar os resultados e ver o quão bem ele se aproxima de uma distribuição normal real (classifique suas amostras 1..x, transforme as classificações em proporções de contagem total de x, ou seja, quantas amostras, obtenha os valores z e plote-os. Uma linha reta para cima é o resultado desejado).
fonte
Use
std::tr1::normal_distribution
.O namespace std :: tr1 não faz parte do boost. É o namespace que contém as adições de biblioteca do C ++ Technical Report 1 e está disponível em compiladores Microsoft e gcc atualizados, independentemente do boost.
fonte
É assim que você gera os exemplos em um compilador C ++ moderno.
fonte
generator
deve realmente ser semeado.Você pode usar o GSL . Alguns exemplos completos são fornecidos para demonstrar como usá-lo.
fonte
Dê uma olhada em: http://www.cplusplus.com/reference/random/normal_distribution/ . É a maneira mais simples de produzir distribuições normais.
fonte
Se estiver usando C ++ 11, você pode usar
std::normal_distribution
:Existem muitas outras distribuições que você pode usar para transformar a saída do mecanismo de números aleatórios.
fonte
Segui a definição do PDF fornecida em http://www.mathworks.com/help/stats/normal-distribution.html e cheguei a isto:
Talvez não seja a melhor abordagem, mas é bastante simples.
fonte
rand()
deRANDU
retornar um zero, pois Ln (0) é indefinido.cos(2*pi*rand/RAND_MAX)
, enquanto você multiplica por(rand()%2 ? -1.0 : 1.0)
.A lista de perguntas frequentes do comp.lang.c compartilha três maneiras diferentes de gerar facilmente números aleatórios com uma distribuição Gaussiana.
Você pode dar uma olhada: http://c-faq.com/lib/gaussian.html
fonte
Implementação do Box-Muller:
fonte
Existem vários algoritmos para a distribuição normal cumulativa inversa. Os mais populares em finanças quantitativas são testados em http://chasethedevil.github.io/post/monte-carlo--inverse-cumulative-normal-distribution/
Na minha opinião, não há muito incentivo para usar algo diferente do algoritmo AS241 de Wichura : é uma máquina de precisão, confiável e rápido. Os gargalos raramente estão na geração de números aleatórios gaussianos.
Além disso, mostra a desvantagem das abordagens do tipo Zigurate.
A principal resposta aqui defende a Box-Müller, você deve estar ciente de que ela tem deficiências conhecidas. Cito https://www.sciencedirect.com/science/article/pii/S0895717710005935 :
fonte
1) A maneira graficamente intuitiva de gerar números aleatórios gaussianos é usando algo semelhante ao método de Monte Carlo. Você geraria um ponto aleatório em uma caixa ao redor da curva gaussiana usando seu gerador de números pseudo-aleatórios em C. Você pode calcular se esse ponto está dentro ou abaixo da distribuição gaussiana usando a equação da distribuição. Se esse ponto estiver dentro da distribuição gaussiana, você obteve seu número aleatório gaussiano como o valor x do ponto.
Este método não é perfeito porque tecnicamente a curva gaussiana segue em direção ao infinito, e você não poderia criar uma caixa que se aproxima do infinito na dimensão x. Mas a curva Guassiana se aproxima de 0 na dimensão y muito rápido, então eu não me preocuparia com isso. A restrição do tamanho de suas variáveis em C pode ser mais um fator limitante para sua precisão.
2) Outra forma seria usar o Teorema do Limite Central, que afirma que quando variáveis aleatórias independentes são adicionadas, elas formam uma distribuição normal. Mantendo esse teorema em mente, você pode aproximar um número aleatório gaussiano adicionando uma grande quantidade de variáveis aleatórias independentes.
Esses métodos não são os mais práticos, mas isso é esperado quando você não deseja usar uma biblioteca pré-existente. Tenha em mente que esta resposta vem de alguém com pouca ou nenhuma experiência em cálculo ou estatística.
fonte
Método de Monte Carlo A maneira mais intuitiva de fazer isso seria usar um método de monte carlo. Pegue um intervalo adequado -X, + X. Valores maiores de X resultarão em uma distribuição normal mais precisa, mas leva mais tempo para convergir. uma. Escolha um número aleatório z entre -X a X. b. Mantenha a probabilidade de
N(z, mean, variance)
onde N é a distribuição gaussiana. Solte caso contrário e volte para a etapa (a).fonte
Dê uma olhada no que encontrei.
Esta biblioteca usa o algoritmo Zigurate.
fonte
O computador é um dispositivo determinístico. Não há aleatoriedade no cálculo. Além disso, o dispositivo aritmético na CPU pode avaliar a soma sobre algum conjunto finito de números inteiros (realizando avaliação em campo finito) e conjunto finito de números racionais reais. E também executou operações bit a bit. A matemática trata de conjuntos maiores como [0,0, 1,0] com número infinito de pontos.
Você pode ouvir algum fio dentro do computador com algum controlador, mas teria distribuição uniforme? Eu não sei. Mas se for assumido que seu sinal é o resultado de acumular valores enormes quantidades de variáveis aleatórias independentes, então você receberá variáveis aleatórias distribuídas aproximadamente normais (Isso foi provado na Teoria da Probabilidade)
Existem algoritmos chamados - gerador pseudo aleatório. Como eu senti, o objetivo do gerador pseudo aleatório é emular a aleatoriedade. E o critério de goodnes é: - a distribuição empírica é convergente (em algum sentido - pontual, uniforme, L2) para teórico - os valores que você recebe do gerador aleatório parecem ser independentes. Claro que não é verdade do 'ponto de vista real', mas presumimos que seja verdade.
Um dos métodos populares - você pode somar 12 irv com distribuições uniformes ... Mas para ser honesto durante a derivação Teorema do Limite Central com ajuda da Transformada de Fourier, Série de Taylor, é necessário ter n -> + inf suposições algumas vezes. Então, por exemplo, teoricamente - Pessoalmente, não entendo como as pessoas realizam a soma de 12 IRV com distribuição uniforme.
Tive teoria da probabilidade na universidade. E, particularmente para mim, é apenas uma questão de matemática. Na universidade, vi o seguinte modelo:
Assim como fazer foi apenas um exemplo, acho que existem outras formas de implementá-lo.
Prova de que está correto pode ser encontrada neste livro "Moscow, BMSTU, 2004: XVI Probability Theory, Example 6.12, p.246-247" de Krishchenko Alexander Petrovich ISBN 5-7038-2485-0
Infelizmente não sei da existência de tradução deste livro para o inglês.
fonte