랜덤 가우스 변수


118

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


http://mathworld.wolfram.com/Box-MullerTransformation.html 두 개의 랜덤 변수를 사용하여 가우스 분포를 따라 랜덤 값을 생성 할 수 있습니다. 그것은 전혀 어려운 일이 아닙니다.
Jarrett Meyer

1
정규 분포 (복잡한 CDF로 인해)에는 즉시 유용하지 않지만 다른 많은 분포에는 유용한 수학적 결과를 추가하고 싶습니다. Random.NextDouble()모든 분포의 CDF의 역 에 [0,1] (와 함께 ) 에 균일하게 분포 된 난수를 넣으면 해당 분포를 따르는 난수를 얻게됩니다. 응용 프로그램에 정확하게 정규 분포 된 변수가 필요하지 않은 경우 로지스틱 분포는 정규 분포에 매우 가깝고 쉽게 반전 할 수있는 CDF를 갖습니다.
Ozzah

1
MedallionRandom NuGet 패키지 (A)로부터 정상 분산 값을 검색하기위한 확장 방법에 포함 Random박스 뮬러 변환하여 (아래에 언급 여러 응답).
ChaseMedallion

답변:


181

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)

3
나는 그것을 테스트하고 MathNet의 Mersenne Twister RNG 및 NormalDistribution과 비교했습니다. 귀하의 버전은 두 배 이상 빠르며 최종 결과는 기본적으로 동일합니다 ( "종"의 육안 검사).
Johann Gerell

4
@Johann, 순수한 속도를 찾고 있다면 Zigorat 알고리즘 이 일반적으로 가장 빠른 접근 방식으로 인식됩니다. 또한 위의 접근 방식은 한 호출에서 다음 호출로 값을 전달하여 더 빠르게 만들 수 있습니다.
Drew Noakes 2011 년

안녕하세요, stdDev변수 는 무엇 으로 설정해야합니까? 특정 요구 사항에 맞게 구성 할 수 있지만 한계 (예 : 최대 / 최소 값)가 있습니까?
hofnarwillie

@hofnarwillie stdDev는 정규 분포의 척도 모수이며 양수일 수 있습니다. 크기가 클수록 생성 된 숫자가 더 많이 분산됩니다. 표준 정규 분포의 경우 매개 변수 mean = 0 및 stdDev = 1을 사용합니다.
yoyoyoyosef

1
@ 잭 나는 그렇게 생각하지 않는다. 단지 -2 * Math.Log (U1)가 SQRT 내부 및 로그 항상 U1 사람 제외 또는 0이된다 <= 1
yoyoyoyosef

63

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

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

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

아무도 뻔뻔한 플러그에 신경 쓰지 않기를 바랍니다.

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

여기에 이미지 설명 입력


귀하의 확장 수업에는 제가 찾고 있던 몇 가지가 있습니다! 감사!
토마스

1
NextGaussian 방법에 작은 버그가 있습니다. NextDouble () 0.0보다 크거나 같고 1.0보다 작은 임의의 부동 소수점 숫자를 반환합니다. 따라서 u1 = 1.0-NextDouble () .... 다른 log (0)이 터질 것입니다
Mitch Wheat

21

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에서 설명서를 찾을 수 있습니다.


좋은 대답입니다! 이 함수는 MathNet.Numerics 패키지의 NuGet에서 사용할 수 있습니다 . 항상 자신을 굴릴 필요가 없습니다.
jpmc26

8

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

그래도 몇 가지 -ve 값을 얻었습니다. 누군가가 무엇이 잘못되었는지 확인할 수 있습니까?
mk7

@ mk7, 0을 중심으로하는 가우스 확률 함수는 양수 값을 제공하는 것과 마찬가지로 음수 값을 제공 할 가능성이 높습니다.
드류 녹스

네가 옳아! 가우시안 PDF를 사용하여 일반적인 인구의 체중 목록을 얻고 싶으므로 mu를 75 [kg]로, 시그마를 10으로 설정합니다. 생성을 위해 GaussianRandom의 새 인스턴스를 설정해야합니까? 모든 무작위 가중치?
mk7

하나의 인스턴스에서 드로잉 샘플을 유지할 수 있습니다.
Drew Noakes


4

다음은 정규 분포 된 랜덤 변수를 생성하기위한 또 다른 빠르고 더러운 솔루션입니다 . 임의의 점 (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 의 간격을 곡선이 맞아야하는 경계 상자로 생각하십시오 .


4
Tangenial는, 그러나 이것은 ... 실제로 내가 대신 _sigma 또는 _phi 같은 바보 같은 뭔가의 변수에 대한 유니 코드 기호를 사용할 수 있습니다 실현 한 것은 이번이 처음이다
Slothario

@Slothario 나는 '무언가 바보'를 사용하는 모든 곳의 개발자에게 감사드립니다 : |
user2864740

2

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

2

@Noakes 및 @Hameer의 답변을 확장하여 'Gaussian'클래스도 구현했지만 메모리 공간을 단순화하기 위해 기본 Next (), NextDouble ()을 호출 할 수 있도록 Random 클래스의 자식으로 만들었습니다. , 등을 처리하기 위해 추가 Random 객체를 만들지 않고도 Gaussian 클래스에서 사용할 수 있습니다. 또한 _available 및 _nextgauss 전역 클래스 속성을 제거했습니다.이 클래스는 인스턴스 기반이므로 필요하지 않았기 때문에 각 스레드에 고유 한 가우스 개체를 제공하면 스레드로부터 안전해야합니다. 또한 모든 런타임 할당 변수를 함수에서 이동하여 클래스 속성으로 만들었습니다. 이렇게하면 4 개의 double이 이론적으로 개체가 파괴 될 때까지 할당 해제되지 않아야하기 때문에 메모리 관리자에 대한 호출 수가 줄어 듭니다.

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

지역 변수도 친구입니다.
user2864740

1

Drew Noakes의 답변을 확장하여 Box-Muller보다 더 나은 성능 (약 50-75 % 더 빠름)이 필요한 경우 Colin Green은 C #에서 Ziggurat 알고리즘 구현을 공유했으며 여기에서 찾을 수 있습니다.

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

Ziggurat는 룩업 테이블을 사용하여 곡선에서 충분히 멀리 떨어진 값을 처리하므로 빠르게 수락하거나 거부합니다. 약 2.5 %의 시간 동안 숫자가 곡선의 어느쪽에 있는지 결정하기 위해 추가 계산을 수행해야합니다.


0

Infer.NET을 사용해 볼 수 있습니다. 하지만 아직 상용 라이센스가 없습니다. 여기 링크가 있습니다

내 Microsoft 연구를 개발 한 .NET의 확률 적 프레임 워크입니다. 그들은 Bernoulli, Beta, Gamma, Gaussian, Poisson의 배포판을위한 .NET 유형을 가지고 있으며 아마도 제가 빠 뜨렸을 것입니다.

그것은 당신이 원하는 것을 성취 할 수 있습니다. 감사.


0

이것은 내 간단한 Box Muller에서 영감을 얻은 구현입니다. 필요에 맞게 해상도를 높일 수 있습니다. 이것은 나에게 잘 작동하지만 이것은 제한된 범위 근사치이므로 꼬리가 닫혀 있고 유한하다는 것을 명심하십시오. 그러나 확실히 필요에 따라 확장 할 수 있습니다.

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

이것은 내 간단한 Box Muller에서 영감을 얻은 구현입니다. 필요에 맞게 해상도를 높일 수 있습니다. 이것은 매우 빠르고 간단하며 작업을 완료하기 위해 대략적인 가우시안 유형의 확률 밀도 함수가 필요한 신경망 앱에서 작동합니다. 누군가가 시간과 CPU주기를 절약하는 데 도움이되기를 바랍니다. 이것은 나에게 잘 작동하지만 이것은 제한된 범위 근사치이므로 꼬리가 닫히고 유한하지만 확실히 필요에 따라 확장 할 수 있습니다.
Daniel Howard

1
안녕하세요 Daniel, 귀하의 의견에 대한 설명을 답변 자체에 통합하는 편집을 제안했습니다. 또한 답변에서 실제 코드를 주석 처리 한 '//'도 제거합니다. 당신이 원하는 경우가 :) 거부됩니다 경우 / 편집을 직접 할 수
mbrig

-1

나는 없다고 생각합니다. 그리고 나는 프레임 워크가 이미 충분히 부풀어 있기 때문에 그런 특별한 기능이 더 많이 채워지지 않았 으면 좋겠다.

http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspxhttp://www.vbforums.com/showthread.php?t=488959 에서 타사 .NET 솔루션을 살펴보십시오 .


7
가우스 분포는 언제부터 '전문화'되었습니까? AJAX 또는 DataTables보다 훨씬 일반적입니다.
TraumaPony

@TraumaPony : 정기적으로 AJAX를 사용하는 것보다 더 많은 개발자가 Gaussian 배포판을 사용하도록 제안하려고하십니까?
David Arno

3
혹시; 내가 말하는 것은 훨씬 더 전문화되어 있다는 것입니다. 하나의 유스 웹 앱만 있습니다. 가우스 분포는 무관 한 수많은 용도를 가지고 있습니다.
TraumaPony

@DavidArno, 더 적은 기능이 프레임 워크를 향상 시킨다고 진지하게 제안하고 있습니까?
Jodrell

1
@Jodrell, 구체적인 예를 들자면 MVC를 메인 .NET 프레임 워크의 일부가 아닌 별도의 프레임 워크로 만든 결정이 좋은 것이라고 생각합니다.
David Arno
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.