Gostaria apenas de adicionar um resultado matemático que não é imediatamente útil para distribuições normais (devido ao CDF complexo), mas é útil para muitas outras distribuições. Se você colocar números aleatórios uniformemente distribuídos em [0,1] (com Random.NextDouble()) no inverso do CDF de QUALQUER distribuição, você obterá números aleatórios que seguem ESSA distribuição. Se a sua aplicação não precisa de variáveis distribuídas normalmente com precisão, então a Distribuição Logística é uma aproximação muito próxima do normal e tem um CDF facilmente invertível.
Ozzah
1
O pacote MedallionRandom NuGet contém um método de extensão para recuperar valores normalmente distribuídos de um Randomusando a transformação Box-Muller (mencionada em várias respostas abaixo).
ChaseMedallion
Respostas:
181
A sugestão de Jarrett de usar uma transformação Box-Muller é boa para uma solução rápida e suja. Uma implementação simples:
Random rand =newRandom();//reuse this if you are generating manydouble u1 =1.0-rand.NextDouble();//uniform(0,1] random doublesdouble u2 =1.0-rand.NextDouble();double randStdNormal =Math.Sqrt(-2.0*Math.Log(u1))*Math.Sin(2.0*Math.PI * u2);//random normal(0,1)double randNormal =
mean + stdDev * randStdNormal;//random normal(mean,stdDev^2)
Eu testei e comparei com Mersenne Twister RNG e NormalDistribution da MathNet. Sua versão é duas vezes mais rápida e o resultado final é basicamente o mesmo (inspeção visual dos "sinos").
Johann Gerell,
4
@Johann, se você está procurando por velocidade pura, o algoritmo Zigorat é geralmente reconhecido como a abordagem mais rápida. Além disso, a abordagem acima pode ser feita mais rapidamente transportando um valor de uma chamada para a próxima.
Drew Noakes em
Olá, como deve stdDevser definida a variável? Eu entendo que isso pode ser configurado para requisitos específicos, mas há algum limite (ou seja, valores máx. / Mín.)?
hofnarwillie
@hofnarwillie stdDev é o parâmetro de escala da distribuição normal, que pode ser qualquer número positivo. Quanto maior for, mais dispersos serão os números gerados. Para uma distribuição normal padrão, use os parâmetros mean = 0 e stdDev = 1.
yoyoyoyosef
1
@Jack acho que não. Apenas -2 * Math.Log (u1) está dentro do sqrt, e o log será sempre negativo ou zero, pois u1 <= 1
yoyoyoyosef
62
Esta pergunta parece ter sido movida para cima da geração Gaussiana do Google para .NET, então resolvi postar uma resposta.
Fiz alguns métodos de extensão para a classe .NET Random , incluindo uma implementação da transformação Box-Muller. Uma vez que são extensões, desde que o projeto esteja incluído (ou você referencie a DLL compilada), você ainda pode fazer
var r =newRandom();var x = r.NextGaussian();
Espero que ninguém se importe com o plug desavergonhado.
Amostra de histograma de resultados (um aplicativo de demonstração para desenhar isso está incluído):
Sua classe de extensão tem algumas coisas que eu estava procurando! obrigado!
Thomas,
1
você tem um pequeno bug em seu método NextGaussian. NextDouble () Retorna um número de ponto flutuante aleatório maior ou igual a 0,0 e menor que 1,0. Portanto, você deve ter u1 = 1,0 - NextDouble () .... outro log (0) explodirá
Este recurso está incluído no Java SDK. Sua implementação está disponível como parte da documentação e é facilmente transportada para C # ou outras linguagens .NET.
Se você está procurando velocidade pura, o algoritmo Zigorat é geralmente reconhecido como a abordagem mais rápida.
Enquanto isso, aqui está um invólucro para Randomque fornece uma implementação eficiente do método polar Box Muller:
publicsealedclassGaussianRandom{privatebool _hasDeviate;privatedouble _storedDeviate;privatereadonlyRandom _random;publicGaussianRandom(Random random =null){
_random = random ??newRandom();}/// <summary>/// Obtains normally (Gaussian) distributed random numbers, using the Box-Muller/// transformation. This transformation takes two uniformly distributed deviates/// within the unit circle, and transforms them into two independently/// distributed normal deviates./// </summary>/// <param name="mu">The mean of the distribution. Default is zero.</param>/// <param name="sigma">The standard deviation of the distribution. Default is one.</param>/// <returns></returns>publicdoubleNextGaussian(double mu =0,double sigma =1){if(sigma <=0)thrownewArgumentOutOfRangeException("sigma","Must be greater than zero.");if(_hasDeviate){
_hasDeviate =false;return _storedDeviate*sigma + mu;}double v1, v2, rSquared;do{// two random values between -1.0 and 1.0
v1 =2*_random.NextDouble()-1;
v2 =2*_random.NextDouble()-1;
rSquared = v1*v1 + v2*v2;// ensure within the unit circle}while(rSquared >=1|| rSquared ==0);// calculate polar tranformation for each deviatevar polar =Math.Sqrt(-2*Math.Log(rSquared)/rSquared);// store first deviate
_storedDeviate = v2*polar;
_hasDeviate =true;// return second deviatereturn v1*polar*sigma + mu;}}
Eu obtive alguns valores disso embora. alguém pode verificar o que há de errado?
mk7
@ mk7, uma função de probabilidade gaussiana centrada em torno de zero tem a mesma probabilidade de fornecer valores negativos e positivos.
Drew Noakes em
Você está certo! Já que gostaria de obter uma lista de peso em uma população típica com PDF gaussiano, estou definindo mu para, digamos, 75 [em kg] e sigma para 10. Preciso definir uma nova instância de GaussianRandom para gerar cada peso aleatório?
mk7
Você pode continuar desenhando amostras de uma instância.
Drew Noakes em
5
Math.NET Iridium também afirma implementar "geradores aleatórios não uniformes (normal, poisson, binomial, ...)".
Mas não está funcionando corretamente. Tentei plotá-lo, Dando número aleatório uniforme.
Nikhil Chilwant
4
Aqui está outra solução rápida e suja para gerar variáveis aleatórias com distribuição normal . Ele desenha algum ponto aleatório (x, y) e verifica se esse ponto está sob a curva de sua função de densidade de probabilidade, caso contrário, repita.
Bônus: Você pode gerar variáveis aleatórias para qualquer outra distribuição (por exemplo, a distribuição exponencial ou distribuição de poisson ) apenas substituindo a função de densidade.
staticRandom _rand =newRandom();publicstaticdoubleDraw(){while(true){// Get random values from interval [0,1]var x = _rand.NextDouble();var y = _rand.NextDouble();// Is the point (x,y) under the curve of the density function?if(y < f(x))return x;}}// Normal (or gauss) distribution functionpublicstaticdouble f(double x,doubleμ=0.5,doubleσ=0.5){return1d/Math.Sqrt(2*σ*σ*Math.PI)*Math.Exp(-((x -μ)*(x -μ))/(2*σ*σ));}
Importante: Selecione o intervalo de y e os parâmetros σ e μ para que a curva da função não seja cortada em seus pontos máximo / mínimo (por exemplo, em x = média). Pense dos intervalos de x e y como uma caixa envolvente, em que a curva deve caber dentro.
Tangenial, mas esta é realmente a primeira vez que percebi que você pode usar símbolos Unicode para variáveis em vez de algo estúpido como _sigma ou _phi ...
Slothario
@Slothario Agradeço aos desenvolvedores em todos os lugares por usarem 'algo idiota': |
user2864740
2
Eu gostaria de expandir a resposta de @yoyoyoyosef tornando-a ainda mais rápida e escrevendo uma classe de wrapper. A sobrecarga incorrida pode não significar duas vezes mais rápido, mas acho que deve ser quase duas vezes mais rápido. Não é thread-safe, entretanto.
Expandindo as respostas de @Noakes e @Hameer, também implementei uma classe 'Gaussiana', mas para simplificar o espaço de memória, tornei-a filha da classe Random para que você também possa chamar o básico Next (), NextDouble () , etc da classe Gaussian também sem ter que criar um objeto Random adicional para manipulá-lo. Também eliminei as propriedades da classe global _available e _nextgauss, pois não as considerava necessárias, uma vez que esta classe é baseada em instância, deve ser thread-safe, se você der a cada thread seu próprio objeto Gaussiano. Também movi todas as variáveis alocadas em tempo de execução para fora da função e as tornei propriedades de classe, isso reduzirá o número de chamadas para o gerenciador de memória, uma vez que as 4 duplas teoricamente nunca devem ser desalocadas até que o objeto seja destruído.
publicclassGaussian:Random{privatedouble u1;privatedouble u2;privatedouble temp1;privatedouble temp2;publicGaussian(int seed):base(seed){}publicGaussian():base(){}/// <summary>/// Obtains normally (Gaussian) distrubuted random numbers, using the Box-Muller/// transformation. This transformation takes two uniformly distributed deviates/// within the unit circle, and transforms them into two independently distributed normal deviates./// </summary>/// <param name="mu">The mean of the distribution. Default is zero</param>/// <param name="sigma">The standard deviation of the distribution. Default is one.</param>/// <returns></returns>publicdoubleRandomGauss(double mu =0,double sigma =1){if(sigma <=0)thrownewArgumentOutOfRangeException("sigma","Must be greater than zero.");
u1 =base.NextDouble();
u2 =base.NextDouble();
temp1 =Math.Sqrt(-2*Math.Log(u1));
temp2 =2*Math.PI * u2;return mu + sigma*(temp1 *Math.Cos(temp2));}}
Expandindo a resposta de Drew Noakes, se você precisa de melhor desempenho do que Box-Muller (cerca de 50-75% mais rápido), Colin Green compartilhou uma implementação do algoritmo Zigurate em C #, que você pode encontrar aqui:
O Zigurate usa uma tabela de pesquisa para lidar com valores que caem suficientemente longe da curva, os quais ele aceitará ou rejeitará rapidamente. Em torno de 2,5% das vezes, ele precisa fazer cálculos adicionais para determinar em que lado da curva está um número.
Você pode tentar o Infer.NET. Porém, ainda não é licenciado comercialmente. Aqui está o link
É uma estrutura probabilística para .NET desenvolvida em minha pesquisa da Microsoft. Eles têm tipos .NET para distribuições de Bernoulli, Beta, Gamma, Gaussian, Poisson e provavelmente mais alguns que deixei de fora.
Esta é a minha implementação simples inspirada no Box Muller. Você pode aumentar a resolução para atender às suas necessidades. Embora funcione muito bem para mim, essa é uma aproximação de intervalo limitado, então tenha em mente que as caudas são fechadas e finitas, mas certamente você pode expandi-las conforme necessário.
Esta é a minha implementação simples inspirada no Box Muller. Você pode aumentar a resolução para atender às suas necessidades. Isso é muito rápido, simples e funciona para meus aplicativos de rede neural que precisam de um tipo gaussiano aproximado de função de densidade de probabilidade para fazer o trabalho. Espero que ajude alguém a economizar tempo e ciclos de CPU. Embora funcione muito bem para mim, essa é uma aproximação de intervalo limitado, então tenha em mente que as caudas são fechadas e finitas, mas certamente você pode expandi-las conforme necessário.
Daniel Howard
1
Olá Daniel, sugeri uma edição que incorpora a descrição do seu comentário na própria resposta. Ele também remove o '//' que estava comentando o código real em sua resposta. Você mesmo pode fazer a edição se quiser / se for rejeitada :)
mbrig
-1
Acho que não. E eu realmente espero que não, já que o framework já está inchado o suficiente, sem essa funcionalidade especializada preenchendo-o ainda mais.
Desde quando a distribuição gaussiana é 'especializada'? É muito mais geral do que, digamos, AJAX ou DataTables.
TraumaPony de
@TraumaPony: você está seriamente tentando sugerir que mais desenvolvedores usem distribuição Gaussiana do que AJAX regularmente?
David Arno,
3
Possivelmente; o que estou dizendo é que é muito mais especializado. Ele só tem um uso - aplicativos da web. As distribuições gaussianas têm um número incrível de usos não relacionados.
TraumaPony
@DavidArno, você está sugerindo seriamente que menos funcionalidade melhora um framework.
Jodrell,
1
@Jodrell, para citar um exemplo específico, acho que a decisão de tornar o MVC uma estrutura separada, em vez de parte da estrutura .NET principal, foi boa.
Random.NextDouble()
) no inverso do CDF de QUALQUER distribuição, você obterá números aleatórios que seguem ESSA distribuição. Se a sua aplicação não precisa de variáveis distribuídas normalmente com precisão, então a Distribuição Logística é uma aproximação muito próxima do normal e tem um CDF facilmente invertível.Random
usando a transformação Box-Muller (mencionada em várias respostas abaixo).Respostas:
A sugestão de Jarrett de usar uma transformação Box-Muller é boa para uma solução rápida e suja. Uma implementação simples:
fonte
stdDev
ser definida a variável? Eu entendo que isso pode ser configurado para requisitos específicos, mas há algum limite (ou seja, valores máx. / Mín.)?Esta pergunta parece ter sido movida para cima da geração Gaussiana do Google para .NET, então resolvi postar uma resposta.
Fiz alguns métodos de extensão para a classe .NET Random , incluindo uma implementação da transformação Box-Muller. Uma vez que são extensões, desde que o projeto esteja incluído (ou você referencie a DLL compilada), você ainda pode fazer
Espero que ninguém se importe com o plug desavergonhado.
Amostra de histograma de resultados (um aplicativo de demonstração para desenhar isso está incluído):
fonte
Math.NET fornece essa funcionalidade. Veja como:
Você pode encontrar a documentação aqui: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm
fonte
Criei uma solicitação para esse recurso no Microsoft Connect. Se é algo que procura, vote e aumente a sua visibilidade.
https://connect.microsoft.com/VisualStudio/feedback/details/634346/guassian-normal-distribution-random-numbers
Este recurso está incluído no Java SDK. Sua implementação está disponível como parte da documentação e é facilmente transportada para C # ou outras linguagens .NET.
Se você está procurando velocidade pura, o algoritmo Zigorat é geralmente reconhecido como a abordagem mais rápida.
Não sou um especialista neste tópico - descobri a necessidade disso ao implementar um filtro de partículas para minha biblioteca de futebol robótico simulado RoboCup 3D e fiquei surpreso quando isso não foi incluído na estrutura.
Enquanto isso, aqui está um invólucro para
Random
que fornece uma implementação eficiente do método polar Box Muller:fonte
Math.NET Iridium também afirma implementar "geradores aleatórios não uniformes (normal, poisson, binomial, ...)".
fonte
Aqui está outra solução rápida e suja para gerar variáveis aleatórias com distribuição normal . Ele desenha algum ponto aleatório (x, y) e verifica se esse ponto está sob a curva de sua função de densidade de probabilidade, caso contrário, repita.
Bônus: Você pode gerar variáveis aleatórias para qualquer outra distribuição (por exemplo, a distribuição exponencial ou distribuição de poisson ) apenas substituindo a função de densidade.
Importante: Selecione o intervalo de y e os parâmetros σ e μ para que a curva da função não seja cortada em seus pontos máximo / mínimo (por exemplo, em x = média). Pense dos intervalos de x e y como uma caixa envolvente, em que a curva deve caber dentro.
fonte
Eu gostaria de expandir a resposta de @yoyoyoyosef tornando-a ainda mais rápida e escrevendo uma classe de wrapper. A sobrecarga incorrida pode não significar duas vezes mais rápido, mas acho que deve ser quase duas vezes mais rápido. Não é thread-safe, entretanto.
fonte
Expandindo as respostas de @Noakes e @Hameer, também implementei uma classe 'Gaussiana', mas para simplificar o espaço de memória, tornei-a filha da classe Random para que você também possa chamar o básico Next (), NextDouble () , etc da classe Gaussian também sem ter que criar um objeto Random adicional para manipulá-lo. Também eliminei as propriedades da classe global _available e _nextgauss, pois não as considerava necessárias, uma vez que esta classe é baseada em instância, deve ser thread-safe, se você der a cada thread seu próprio objeto Gaussiano. Também movi todas as variáveis alocadas em tempo de execução para fora da função e as tornei propriedades de classe, isso reduzirá o número de chamadas para o gerenciador de memória, uma vez que as 4 duplas teoricamente nunca devem ser desalocadas até que o objeto seja destruído.
fonte
Expandindo a resposta de Drew Noakes, se você precisa de melhor desempenho do que Box-Muller (cerca de 50-75% mais rápido), Colin Green compartilhou uma implementação do algoritmo Zigurate em C #, que você pode encontrar aqui:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
O Zigurate usa uma tabela de pesquisa para lidar com valores que caem suficientemente longe da curva, os quais ele aceitará ou rejeitará rapidamente. Em torno de 2,5% das vezes, ele precisa fazer cálculos adicionais para determinar em que lado da curva está um número.
fonte
Você pode tentar o Infer.NET. Porém, ainda não é licenciado comercialmente. Aqui está o link
É uma estrutura probabilística para .NET desenvolvida em minha pesquisa da Microsoft. Eles têm tipos .NET para distribuições de Bernoulli, Beta, Gamma, Gaussian, Poisson e provavelmente mais alguns que deixei de fora.
Pode realizar o que você deseja. Obrigado.
fonte
Esta é a minha implementação simples inspirada no Box Muller. Você pode aumentar a resolução para atender às suas necessidades. Embora funcione muito bem para mim, essa é uma aproximação de intervalo limitado, então tenha em mente que as caudas são fechadas e finitas, mas certamente você pode expandi-las conforme necessário.
fonte
Acho que não. E eu realmente espero que não, já que o framework já está inchado o suficiente, sem essa funcionalidade especializada preenchendo-o ainda mais.
Dê uma olhada em http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx e http://www.vbforums.com/showthread.php?t=488959 para soluções .NET de terceiros.
fonte