Variáveis ​​Gaussianas Aleatórias

Existe uma class na biblioteca padrão do .NET que me dá a funcionalidade para criar variables ​​aleatórias que seguem a distribuição gaussiana?

A sugestão de Jarret de usar uma transformada Box-Muller é boa para uma solução rápida e suja. Uma implementação simples:

 Random rand = new Random(); //reuse this if you are generating many double u1 = 1.0-rand.NextDouble(); //uniform(0,1] random doubles double 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) 

Essa questão parece ter se movido sobre a geração Gaussiana do Google para .NET, então pensei em postar uma resposta.

Eu fiz alguns methods de extensão para a class Random .NET , incluindo uma implementação da transformação Box-Muller. Já que são extensões, desde que o projeto seja incluído (ou você faz referência à DLL compilada), você ainda pode fazer

 var r = new Random(); var x = r.NextGaussian(); 

Espero que ninguém se importe com o plug sem vergonha.

Exemplo de histograma de resultados (um aplicativo de demonstração para desenhar isso está incluído):

insira a descrição da imagem aqui

http://mathworld.wolfram.com/Box-MullerTransformation.html

Usando duas variables ​​aleatórias, você pode gerar valores randoms ao longo de uma distribuição gaussiana. Não é uma tarefa difícil.

Math.NET fornece essa funcionalidade. Veja como:

 double mean = 100; double stdDev = 10; MathNet.Numerics.Distributions.Normal normalDist = new Normal(mean, stdDev); double randomGaussianValue= normalDist.Sample(); 

Você pode encontrar a documentação aqui: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

Eu criei uma solicitação para esse recurso no Microsoft Connect. Se isso é algo que você está procurando, vote e aumente sua visibilidade.

https://connect.microsoft.com/VisualStudio/feedback/details/634346/guassyian-normal-distribution-random-numbers

Esse recurso está incluído no Java SDK. Sua implementação está disponível como parte da documentação e é facilmente portada para C # ou outras linguagens .NET.

Se você está procurando por velocidade pura, então o algoritmo Zigorat é geralmente reconhecido como a abordagem mais rápida.

Eu não sou um especialista neste assunto – eu me deparei com a necessidade disso enquanto eu estava implementando um filtro de partículas para minha biblioteca robótica de futebol simulado do RoboCup 3D e fiquei surpreso quando isso não foi incluído no framework.


Enquanto isso, aqui está um wrapper para Random que fornece uma implementação eficiente do método polar Box Muller:

 public sealed class GaussianRandom { private bool _hasDeviate; private double _storedDeviate; private readonly Random _random; public GaussianRandom(Random random = null) { _random = random ?? new Random(); } ///  /// 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. ///  /// The mean of the distribution. Default is zero. /// The standard deviation of the distribution. Default is one. ///  public double NextGaussian(double mu = 0, double sigma = 1) { if (sigma <= 0) throw new ArgumentOutOfRangeException("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 deviate var polar = Math.Sqrt(-2*Math.Log(rSquared)/rSquared); // store first deviate _storedDeviate = v2*polar; _hasDeviate = true; // return second deviate return v1*polar*sigma + mu; } } 

O Math.NET Iridium também pretende implementar “geradores randoms não uniformes (normal, poisson, binomial, …)”.

Aqui está outra solução rápida e suja para gerar variables ​​aleatórias que são distribuídas normalmente . Ele desenha um ponto random (x, y) e verifica se esse ponto está sob a curva da sua function de densidade de probabilidade, caso contrário, repita.

Bônus: Você pode gerar variables ​​aleatórias para qualquer outra distribuição (por exemplo, a distribuição exponencial ou distribuição de poisson ) apenas substituindo a function de densidade.

  static Random _rand = new Random(); public static double Draw() { 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 function public static double f(double x, double μ = 0.5, double σ = 0.5) { return 1d / 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 function não seja cortada em seus pontos máximo / mínimo (por exemplo, em x = média). Pense nos intervalos de x e y como uma checkbox delimitadora, na qual a curva deve se encheckboxr.

Eu gostaria de expandir a resposta do @ yoyoyoyosef tornando-o ainda mais rápido e escrevendo uma class wrapper. A sobrecarga incorrida pode não significar duas vezes mais rápido, mas acho que deveria ser quase duas vezes mais rápido. Não é thread-safe, no entanto.

 public class Gaussian { private bool _available; private double _nextGauss; private Random _rng; public Gaussian() { _rng = new Random(); } public double RandomGauss() { if (_available) { _available = false; return _nextGauss; } double u1 = _rng.NextDouble(); double u2 = _rng.NextDouble(); double temp1 = Math.Sqrt(-2.0*Math.Log(u1)); double temp2 = 2.0*Math.PI*u2; _nextGauss = temp1 * Math.Sin(temp2); _available = true; return temp1*Math.Cos(temp2); } public double RandomGauss(double mu, double sigma) { return mu + sigma*RandomGauss(); } public double RandomGauss(double sigma) { return sigma*RandomGauss(); } } 

Expandindo a resposta de Drew Noakes, se você precisa de um desempenho melhor do que o Box-Muller (cerca de 50-75% mais rápido), Colin Green compartilhou uma implementação do algoritmo Ziggurat em C #, que você pode encontrar aqui:

http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html

O Ziggurat usa uma tabela de consulta para manipular valores que caem suficientemente longe da curva, que serão rapidamente aceitos ou rejeitados. Em torno de 2,5% do tempo, ele precisa fazer mais cálculos para determinar em qual lado da curva um número está ativo.

Você poderia tentar o Infer.NET. Ainda não é licenciado comercialmente. Aqui está o link

É uma estrutura probabilística para o .NET desenvolver minha pesquisa da Microsoft. Eles têm tipos .NET para distribuições de Bernoulli, Beta, Gamma, Gaussian, Poisson e, provavelmente, mais algumas que eu deixei de fora.

Pode conseguir o que você quer. Obrigado.

Esta é a minha implementação simples inspirada em Box Muller. Você pode aumentar a resolução para atender às suas necessidades. Embora isso funcione muito bem para mim, essa é uma aproximação de alcance limitado, portanto, tenha em mente que as caudas são fechadas e finitas, mas certamente você pode expandi-las conforme necessário.

  // // by Dan // islandTraderFX // copyright 2015 // Siesta Key, FL // // 0.0 3231 ******************************** // 0.1 1981 ******************* // 0.2 1411 ************** // 0.3 1048 ********** // 0.4 810 ******** // 0.5 573 ***** // 0.6 464 **** // 0.7 262 ** // 0.8 161 * // 0.9 59 //Total: 10000 double g() { double res = 1000000; return random.Next(0, (int)(res * random.NextDouble()) + 1) / res; } public static class RandomProvider { public static int seed = Environment.TickCount; private static ThreadLocal randomWrapper = new ThreadLocal(() => new Random(Interlocked.Increment(ref seed)) ); public static Random GetThreadRandom() { return randomWrapper.Value; } } 

Expandindo as respostas do @Noakes e do @Hameer, eu também implementei uma class ‘Gaussiana’, mas para simplificar o espaço da memory, tornei-a filha da class Random para que você também possa chamar o básico Next (), NextDouble () , etc da class Gaussian também sem ter que criar um object Random adicional para lidar com isso. Eu também eliminei as propriedades de class global _available e _nextgauss, já que eu não as via como necessárias, já que essa class é baseada em instâncias, deve ser thread-safe, se você der a cada thread seu próprio object Gaussian. Também movi todas as variables ​​alocadas em tempo de execução para fora da function e as tornei propriedades de class, isso reduzirá o número de chamadas para o gerenciador de memory, já que as 4 duplas teoricamente nunca devem ser desalocadas até que o object seja destruído.

 public class Gaussian : Random { private double u1; private double u2; private double temp1; private double temp2; public Gaussian(int seed):base(seed) { } public Gaussian() : base() { } ///  /// 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. ///  /// The mean of the distribution. Default is zero /// The standard deviation of the distribution. Default is one. ///  public double RandomGauss(double mu = 0, double sigma = 1) { if (sigma <= 0) throw new ArgumentOutOfRangeException("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)); } } 

Eu não acho que haja. E eu realmente espero que não haja, já que a estrutura já está inchada o suficiente, sem que essa funcionalidade especializada a preencha 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.