랜덤 가우스 변수


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 와 매개 변수 σ 및 μ 의 간격을 선택합니다 . x 와 y 의 간격을 곡선이 맞아야하는 경계 상자로 생각하십시오 .


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.aspx 및 http://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.