Random Gaussian Variables

2020-01-23 10:30发布

Is there a class in the standard library of .NET that gives me the functionality to create random variables that follow Gaussian distribution?

12条回答
仙女界的扛把子
2楼-- · 2020-01-23 10:57

Expanding off of @Noakes and @Hameer's answers, I have also implemented a 'Gaussian' class, but to simplify memory space, I made it a child of the Random class so that you can also call the basic Next(), NextDouble(), etc from the Gaussian class as well without having to create an additional Random object to handle it. I also eliminated the _available, and _nextgauss global class properties, as I didn't see them as necessary since this class is instance based, it should be thread-safe, if you give each thread its own Gaussian object. I also moved all of the run-time allocated variables out of the function and made them class properties, this will reduce the number of calls to the memory manager since the 4 doubles should theoretically never be de-allocated until the object is destroyed.

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()
    {
    }

    /// <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>

    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));
    }
}
查看更多
小情绪 Triste *
3楼-- · 2020-01-23 10:59

Here is another quick and dirty solution for generating random variables that are normal distributed. It draws some random point (x,y) and checks if this point lies under the curve of your probability density function, otherwise repeat.

Bonus: You can generate random variables for any other distribution (e.g. the exponential distribution or poisson distribution) just by replacing the density function.

    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 * σ * σ));
    }

Important: Select the interval of y and the parameters σ and μ so that the curve of the function is not cutoff at it's maximum/minimum points (e.g. at x=mean). Think of the intervals of x and y as a bounding box, in which the curve must fit in.

查看更多
小情绪 Triste *
4楼-- · 2020-01-23 10:59

This is my simple Box Muller inspired implementation. You can increase the resolution to fit your needs. Although this works great for me, this is a limited range approximation, so keep in mind the tails are closed and finite, but certainly you can expand them as needed.

    //
    // 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<Random> randomWrapper = new ThreadLocal<Random>(() =>
       new Random(Interlocked.Increment(ref seed))
   );

   public static Random GetThreadRandom()
   {
       return randomWrapper.Value;
   }
} 
查看更多
The star\"
5楼-- · 2020-01-23 11:00

This question appears to have moved on top of Google for .NET Gaussian generation, so I figured I'd post an answer.

I've made some extension methods for the .NET Random class, including an implementation of the Box-Muller transform. Since they're extensions, so long as the project is included (or you reference the compiled DLL), you can still do

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

Hope nobody minds the shameless plug.

Sample histogram of results (a demo app for drawing this is included):

enter image description here

查看更多
男人必须洒脱
6楼-- · 2020-01-23 11:01

I don't think there is. And I really hope there isn't, as the framework is already bloated enough, without such specialised functionality filling it even more.

Take a look at http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx and http://www.vbforums.com/showthread.php?t=488959 for a third party .NET solutions though.

查看更多
叼着烟拽天下
7楼-- · 2020-01-23 11:04

Expanding on Drew Noakes's answer, if you need better performance than Box-Muller (around 50-75% faster), Colin Green has shared an implementation of the Ziggurat algorithm in C#, which you can find here:

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

Ziggurat uses a lookup table to handle values that fall sufficiently far from the curve, which it will quickly accept or reject. Around 2.5% of the time, it has to do further calculations to determine which side of the curve a number is on.

查看更多
登录 后发表回答