IT story

랜덤 가우스 변수

hot-time 2020. 8. 5. 07:43
반응형

랜덤 가우스 변수


.NET의 표준 라이브러리에 가우시안 분포를 따르는 임의의 변수를 생성하는 기능을 제공하는 클래스가 있습니까?


Box-Muller 변환 사용에 대한 Jarrett의 제안은 빠르고 더러운 솔루션에 적합합니다. 간단한 구현 :

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)

이 질문은 .NET Gaussian 생성을 위해 Google 위로 이동 한 것으로 보이므로 답변을 게시 할 것으로 생각했습니다.

Box-Muller 변환 구현을 포함 하여 .NET Random 클래스에 대한 몇 가지 확장 메서드를 만들었습니다 . 그것들은 확장이기 때문에 프로젝트가 포함되어 있거나 컴파일 된 DLL을 참조하는 한 여전히 할 수 있습니다

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

누구도 뻔뻔한 플러그를 신경 쓰지 않기를 바랍니다.

결과의 히스토그램 샘플 (이를 그리기위한 데모 앱이 포함되어 있음) :

여기에 이미지 설명을 입력하십시오


Math.NET 은이 기능을 제공합니다. 방법은 다음과 같습니다.

double mean = 100;
double stdDev = 10;

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

여기에서 설명서를 찾을 수 있습니다 : http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm


Microsoft Connect에서 이러한 기능에 대한 요청을 작성했습니다. 이것이 당신이 찾고있는 것이라면 투표하고 가시성을 높이십시오.

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

이 기능은 Java SDK에 포함되어 있습니다. 구현은 설명서의 일부로 제공 되며 C # 또는 다른 .NET 언어로 쉽게 이식됩니다.

순수한 속도를 원한다면 Zigorat 알고리즘 이 일반적으로 가장 빠른 방법으로 인식됩니다.

그래도 나는이 주제에 대해 전문가가 아닙니다. RoboCup 3D 시뮬레이션 로봇 축구 라이브러리를 위한 입자 필터구현 하면서이 필요성에 부딪 쳤고 이것이 프레임 워크에 포함되지 않았을 때 놀랐습니다.


한편, Random박스 뮬러 폴라 방식의 효율적인 구현을 제공하는 래퍼는 다음과 같습니다.

public sealed class GaussianRandom
{
    private bool _hasDeviate;
    private double _storedDeviate;
    private readonly Random _random;

    public GaussianRandom(Random random = null)
    {
        _random = random ?? new Random();
    }

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

Math.NET Iridium 은 또한 "비 균일 랜덤 생성기 (정상, 포아송, 이항 등)"를 구현한다고 주장합니다.


다음은 정규 분포의 랜덤 변수를 생성하는 또 다른 빠르고 더러운 솔루션입니다 . 임의의 점 (x, y)을 그리고이 점이 확률 밀도 함수의 곡선 아래에 있는지 확인하고, 그렇지 않으면 반복합니다.

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.


I'd like to expand upon @yoyoyoyosef's answer by making it even faster, and writing a wrapper class. The overhead incurred may not mean twice as fast, but I think it should be almost twice as fast. It isn't thread-safe, though.

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

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.


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

You could try Infer.NET. It's not commercial licensed yet though. Here is there link

It is a probabilistic framework for .NET developed my Microsoft research. They have .NET types for distributions of Bernoulli, Beta, Gamma, Gaussian, Poisson, and probably some more I left out.

It may accomplish what you want. Thanks.


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;
   }
} 

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.

한 번 봐 가지고 http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspxhttp://www.vbforums.com/showthread.php?t=488959 하지만 타사 .NET 솔루션을.

참고 URL : https://stackoverflow.com/questions/218060/random-gaussian-variables

반응형