랜덤 가우스 변수 따르는 랜덤 변수를

가우스 분포를 따르는 랜덤 변수를 생성하는 기능을 제공하는 .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 시뮬레이션 로봇 축구 라이브러리에 입자 필터 를 구현하는 동안 이것이 필요하다는 것을 알게 되었고 이것이 프레임 워크에 포함되지 않았을 때 놀랐습니다.


한편, RandomBox Muller 극성 방법의 효율적인 구현을 제공하는 래퍼는 다음과 같습니다.

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)을 그리고이 점이 확률 밀도 함수의 곡선 아래에 있는지 확인하고 그렇지 않으면 반복합니다.

보너스 : 밀도 함수를 바꾸는 것만으로 다른 분포 (예 : 지수 분포 또는 포아송 분포 )에 대한 랜덤 변수를 생성 할 수 있습니다 .

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

중요 : 함수의 곡선이 최대 / 최소 지점 (예 : x = 평균)에서 잘리지 않도록 y 와 매개 변수 σμ 의 간격을 선택합니다 . xy 의 간격을 곡선이 맞아야하는 경계 상자로 생각하십시오 .


답변

@yoyoyoyosef의 답변을 더 빠르게 만들고 래퍼 클래스를 작성하여 확장하고 싶습니다. 발생하는 오버 헤드는 두 배 빠르지는 않지만 거의 두 배 빠르다고 생각합니다 . 하지만 스레드로부터 안전하지 않습니다.

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