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 =newRandom();//reuse this if you are generating manydouble u1 =1.0-rand.NextDouble();//uniform(0,1] random doublesdouble 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)
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 =newRandom();var x = r.NextGaussian();
Mam nadzieję, że nikomu nie przeszkadza bezwstydna wtyczka.
Przykładowy histogram wyników (dołączona jest aplikacja demonstracyjna do rysowania):
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
W międzyczasie, oto opakowanie, Randomktóre zapewnia wydajną implementację metody biegunowej Box Muller:
publicsealedclassGaussianRandom{privatebool _hasDeviate;privatedouble _storedDeviate;privatereadonlyRandom _random;publicGaussianRandom(Random random =null){
_random = random ??newRandom();}/// <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>publicdoubleNextGaussian(double mu =0,double sigma =1){if(sigma <=0)thrownewArgumentOutOfRangeException("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 deviatevar polar =Math.Sqrt(-2*Math.Log(rSquared)/rSquared);// store first deviate
_storedDeviate = v2*polar;
_hasDeviate =true;// return second deviatereturn 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.
BUt nie działa poprawnie. Próbowałem to zaplanować, dając mundurowi losowe nie.
Nikhil Chilwant
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.
staticRandom _rand =newRandom();publicstaticdoubleDraw(){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 functionpublicstaticdouble f(double x,doubleμ=0.5,doubleσ=0.5){return1d/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ć.
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.
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.
publicclassGaussian:Random{privatedouble u1;privatedouble u2;privatedouble temp1;privatedouble temp2;publicGaussian(int seed):base(seed){}publicGaussian():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>publicdoubleRandomGauss(double mu =0,double sigma =1){if(sigma <=0)thrownewArgumentOutOfRangeException("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));}}
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:
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.
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.
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.
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.
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.
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.Random
transformacji Boxa-Mullera (wspomnianej w kilku odpowiedziach poniżej).Odpowiedzi:
Sugestia Jarretta dotycząca użycia transformacji Boxa-Mullera jest dobra dla szybkiego i brudnego rozwiązania. Prosta implementacja:
źródło
stdDev
ustawić zmienną? Rozumiem, że można to skonfigurować zgodnie z określonymi wymaganiami, ale czy istnieją jakieś ograniczenia (tj. Wartości maksymalne / minimalne)?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ć
Mam nadzieję, że nikomu nie przeszkadza bezwstydna wtyczka.
Przykładowy histogram wyników (dołączona jest aplikacja demonstracyjna do rysowania):
źródło
Math.NET zapewnia tę funkcjonalność. Oto jak:
Dokumentację można znaleźć tutaj: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm
źródło
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,
Random
które zapewnia wydajną implementację metody biegunowej Box Muller:źródło
Math.NET Iridium twierdzi również, że implementuje „niejednorodne generatory losowe (normalne, poissona, dwumianowe, ...)”.
źródło
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.
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ć.
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło