Losowe zmienne Gaussa


118

Czy w standardowej bibliotece platformy .NET znajduje się klasa, która daje mi możliwość tworzenia zmiennych losowych zgodnych z rozkładem Gaussa?


http://mathworld.wolfram.com/Box-MullerTransformation.html Korzystając z dwóch zmiennych losowych, można wygenerować wartości losowe wzdłuż rozkładu Gaussa. To wcale nie jest trudne zadanie.
Jarrett Meyer

1
Chciałbym tylko dodać wynik matematyczny, który nie jest od razu przydatny w przypadku rozkładów normalnych (z powodu złożonego CDF), ale jest przydatny w wielu innych rozkładach. Jeśli umieścisz równomiernie rozłożone liczby losowe w [0,1] (z Random.NextDouble()) na odwrotność CDF DOWOLNEGO rozkładu, otrzymasz liczby losowe, które następują po TYM rozkładzie. Jeśli Twoja aplikacja nie potrzebuje zmiennych o dokładnie rozkładzie normalnym, wówczas rozkład logistyczny jest bardzo zbliżonym do normalnego i ma łatwo odwracalny współczynnik CDF.
Ozzah

1
Pakiet MedallionRandom NuGet zawiera metodę rozszerzającą do pobierania wartości o rozkładzie normalnym z Randomtransformacji Boxa-Mullera (wspomnianej w kilku odpowiedziach poniżej).
ChaseMedallion

Odpowiedzi:


181

Sugestia Jarretta dotycząca użycia transformacji Boxa-Mullera jest dobra dla szybkiego i brudnego rozwiązania. Prosta implementacja:

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
Przetestowałem to i porównałem z Mersenne Twister RNG i NormalDistribution MathNet. Twoja wersja jest ponad dwa razy szybsza, a efekt końcowy jest w zasadzie taki sam (oględziny "dzwonków").
Johann Gerell

4
@Johann, jeśli szukasz czystej prędkości, algorytm Zigorat jest ogólnie uznawany za najszybsze podejście. Ponadto powyższe podejście można przyspieszyć przenosząc wartość z jednego wywołania do drugiego.
Drew Noakes

Cześć, na co należy stdDevustawić zmienną? Rozumiem, że można to skonfigurować zgodnie z określonymi wymaganiami, ale czy istnieją jakieś ograniczenia (tj. Wartości maksymalne / minimalne)?
hofnarwillie

@hofnarwillie stdDev to parametr skali rozkładu normalnego, który może być dowolną liczbą dodatnią. Im jest większy, tym bardziej rozproszone będą generowane liczby. Dla standardowego rozkładu normalnego użyj parametrów mean = 0 i stdDev = 1.
yoyoyoyosef

1
@Jack, nie sądzę. Tylko -2 * Math.Log (u1) znajduje się wewnątrz sqrt, a log zawsze będzie ujemny lub zerowy, ponieważ u1 <= 1
yoyoyoyosef

63

Wydaje się, że to pytanie przeniosło się do Google w przypadku generacji Gaussa .NET, więc pomyślałem, że opublikuję odpowiedź.

Zrobiłem kilka metod rozszerzających dla klasy .NET Random , w tym implementację transformacji Boxa-Mullera. Ponieważ są to rozszerzenia, o ile projekt jest uwzględniony (lub odwołujesz się do skompilowanej biblioteki DLL), nadal możesz to zrobić

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

Mam nadzieję, że nikomu nie przeszkadza bezwstydna wtyczka.

Przykładowy histogram wyników (dołączona jest aplikacja demonstracyjna do rysowania):

wprowadź opis obrazu tutaj


Twoja klasa rozszerzenia ma kilka rzeczy, których szukałem! dzięki!
Thomas

1
masz mały błąd w swojej metodzie NextGaussian. NextDouble () Zwraca losową liczbę zmiennoprzecinkową, która jest większa lub równa 0,0 i mniejsza niż 1,0. Więc powinieneś mieć u1 = 1.0 - NextDouble () .... inny log (0) wysadzi
Mitch Wheat

21

Math.NET zapewnia tę funkcjonalność. Oto jak:

double mean = 100;
double stdDev = 10;

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

Dokumentację można znaleźć tutaj: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm


Świetna odpowiedź! Ta funkcja jest dostępna w NuGet w pakiecie MathNet.Numerics . Zawsze wspaniale jest nie toczyć własnego.
jpmc26

8

Utworzyłem prośbę o taką funkcję na Microsoft Connect. Jeśli jest to coś, czego szukasz, zagłosuj na to i zwiększ jego widoczność.

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

Ta funkcja jest zawarta w Java SDK. Jego implementacja jest dostępna jako część dokumentacji i można ją łatwo przenieść na C # lub inne języki .NET.

Jeśli szukasz czystej prędkości, algorytm Zigorat jest ogólnie uznawany za najszybsze podejście.

Nie jestem jednak ekspertem w tym temacie - natknąłem się na taką potrzebę podczas wdrażania filtra cząstek stałych do mojej biblioteki symulowanej robotycznej piłki nożnej RoboCup 3D i byłem zaskoczony, że nie zostało to uwzględnione we frameworku.


W międzyczasie, oto opakowanie, Randomktóre zapewnia wydajną implementację metody biegunowej Box 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;
    }
}

Jednak mam z tego kilka wartości. czy ktoś może sprawdzić, co się stało?
mk7

@ mk7, funkcja prawdopodobieństwa Gaussa skupiona wokół zera z takim samym prawdopodobieństwem da wartości ujemne, jak i wartości dodatnie.
Drew Noakes,

Masz rację! Ponieważ chciałbym uzyskać listę wagi w typowej populacji z gaussowskim plikiem PDF, ustawiam mu na, powiedzmy, 75 [w kg] i sigma na 10. Czy muszę ustawić nową instancję GaussianRandom do generowania każda przypadkowa waga?
mk7

Możesz nadal rysować próbki z jednego wystąpienia.
Drew Noakes


4

Oto kolejne szybkie i brudne rozwiązanie do generowania zmiennych losowych o rozkładzie normalnym . Rysuje losowy punkt (x, y) i sprawdza, czy ten punkt leży pod krzywą twojej funkcji gęstości prawdopodobieństwa, w przeciwnym razie powtórz.

Bonus: Możesz wygenerować zmienne losowe dla dowolnego innego rozkładu (np. Rozkładu wykładniczego lub rozkładu poissona ), zastępując funkcję gęstości.

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

Ważne: Wybierz przedział y oraz parametry σ i μ tak, aby krzywa funkcji nie była odcięta w jej punktach maksimum / minimum (np. Przy x = średnia). Pomyśl o przedziałach x i y jak o obwiedni, w której krzywa musi pasować.


4
Tangenial, ale tak naprawdę to pierwszy raz, kiedy zdałem sobie sprawę, że możesz użyć symboli Unicode dla zmiennych zamiast czegoś głupiego, takiego jak _sigma lub _phi ...
Slothario

@Slothario Dziękuję programistom na całym świecie za użycie „czegoś głupiego”: |
user2864740

2

Chciałbym rozwinąć odpowiedź @ yoyoyoyosef, czyniąc ją jeszcze szybszą i pisząc klasę opakowującą. Poniesione narzuty mogą nie oznaczać dwa razy szybciej, ale myślę, że powinno być prawie dwa razy szybsze. Nie jest to jednak bezpieczne dla wątków.

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

Rozwijając odpowiedzi @Noakes i @ Hameer, zaimplementowałem również klasę 'Gaussian', ale aby uprościć przestrzeń pamięci, uczyniłem ją dzieckiem klasy Random, aby można było również wywołać podstawowe Next (), NextDouble () , etc z klasy Gaussa bez konieczności tworzenia dodatkowego obiektu Random do obsługi tego. Wyeliminowałem również właściwości klas globalnych _available i _nextgauss, ponieważ nie uważałem ich za konieczne, ponieważ ta klasa jest oparta na instancji, powinna być bezpieczna dla wątków, jeśli nadasz każdemu wątkowi własny obiekt Gaussa. Usunąłem również wszystkie zmienne przydzielone w czasie wykonywania z funkcji i uczyniłem je właściwościami klas, co zmniejszy liczbę wywołań menedżera pamięci, ponieważ te 4 dublety teoretycznie nigdy nie powinny zostać cofnięte, dopóki obiekt nie zostanie zniszczony.

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

Zmienne lokalne też są przyjaciółmi.
user2864740

1

Rozwijając odpowiedź Drew Noakesa, jeśli potrzebujesz lepszej wydajności niż Box-Muller (około 50-75% szybciej), Colin Green udostępnił implementację algorytmu Ziggurat w C #, którą możesz znaleźć tutaj:

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

Ziggurat używa tabeli przeglądowej do obsługi wartości, które znajdują się wystarczająco daleko od krzywej, którą szybko akceptuje lub odrzuca. W około 2,5% przypadków musi wykonać dalsze obliczenia, aby określić, po której stronie krzywej znajduje się liczba.


0

Możesz wypróbować Infer.NET. Jednak nie jest to jeszcze licencja komercyjna. Tutaj jest link

Jest to probabilistyczny framework dla .NET, który opracowałem w moim badaniu Microsoft. Mają typy .NET dla dystrybucji Bernoulliego, Beta, Gamma, Gaussa, Poissona i prawdopodobnie kilka innych, które pominąłem.

Może osiągnąć to, czego chcesz. Dzięki.


0

To moja prosta realizacja inspirowana Box Mullerem. Możesz zwiększyć rozdzielczość, aby dopasować ją do swoich potrzeb. Chociaż dla mnie to działa świetnie, jest to przybliżenie o ograniczonym zakresie, więc pamiętaj, że ogony są zamknięte i skończone, ale z pewnością możesz je rozszerzyć w razie potrzeby.

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

To jest moja prosta realizacja inspirowana Box Mullerem. Możesz zwiększyć rozdzielczość, aby dopasować ją do swoich potrzeb. Jest to bardzo szybkie, proste i działa dla moich aplikacji sieci neuronowych, które potrzebują przybliżonej funkcji gęstości prawdopodobieństwa typu Gaussa, aby wykonać zadanie. Mam nadzieję, że pomoże to komuś zaoszczędzić czas i cykle procesora. Chociaż działa to świetnie, jest to przybliżenie o ograniczonym zakresie, więc pamiętaj, że ogony są zamknięte i skończone, ale z pewnością możesz je rozszerzyć w razie potrzeby.
Daniel Howard,

1
Hej Daniel, zasugerowałem edycję, która włączy opis z Twojego komentarza do samej odpowiedzi. Usuwa również „//”, który komentował prawdziwy kod w Twojej odpowiedzi. Możesz dokonać edycji samodzielnie, jeśli chcesz / jeśli zostanie odrzucona :)
mbrig

-1

Myślę, że nie ma. I naprawdę mam nadzieję, że tak nie jest, ponieważ framework jest już wystarczająco rozdęty, bez tak wyspecjalizowanej funkcjonalności wypełniającej go jeszcze bardziej.

Spójrz na http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx i http://www.vbforums.com/showthread.php?t=488959 dla rozwiązań .NET innej firmy.


7
Od kiedy dystrybucja Gaussa jest „wyspecjalizowana”? Jest znacznie bardziej ogólny niż, powiedzmy, AJAX lub DataTables.
TraumaPony

@TraumaPony: czy naprawdę starasz się zasugerować większej liczbie programistów używanie dystrybucji Gaussa niż regularnego używania AJAX?
David Arno,

3
Możliwie; mówię, że jest znacznie bardziej wyspecjalizowany. Ma tylko jedno zastosowanie - aplikacje internetowe. Dystrybucje Gaussa mają niesamowitą liczbę niepowiązanych zastosowań.
TraumaPony

@DavidArno, czy poważnie sugerujesz, że mniejsza funkcjonalność poprawia strukturę.
Jodrell

1
@Jodrell, cytując konkretny przykład, myślę, że decyzja o uczynieniu MVC oddzielnym frameworkiem, a nie częścią głównego frameworka .NET, była dobra.
David Arno
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.