Випадкові гауссові змінні


118

Чи є клас у стандартній бібліотеці .NET, який дає мені функціонал для створення випадкових змінних, що слідують за розподілом Гаусса?


http://mathworld.wolfram.com/Box-MullerTransformation.html Використовуючи дві випадкові змінні, ви можете генерувати випадкові значення по Гауссовому розподілу. Це зовсім не складне завдання.
Джарретт Мейєр

1
Я просто хотів би додати математичний результат, який не одразу корисний для звичайних розподілів (через складний CDF), але корисний для багатьох інших дистрибутивів. Якщо ви помістите рівномірно розподілені випадкові числа в [0,1] (з Random.NextDouble()) в інверсію CDF будь-якого розподілу, ви отримаєте випадкові числа, які слідують за ТО. Якщо вашій програмі не потрібні точно нормально розподілені змінні, то логістичний розподіл є дуже близьким наближенням до нормального та має легко обернений CDF.
Озза

1
Пакет MedallionRandom NuGet містить метод розширення для отримання нормально розподілених значень за Randomдопомогою перетворення Box-Muller (згаданого в кількох відповідях нижче).
ChaseMedallion

Відповіді:


181

Пропозиція Джаретта використовувати перетворення Box-Muller добре підходить для швидкого і брудного рішення. Проста реалізація:

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
Я перевірив його і порівняв з Мерсенн Twister RNG і NormalDistribution. Ваша версія більш ніж удвічі швидша, а кінцевий результат в основному однаковий (візуальний огляд «дзвонів»).
Йоганн Герелл

4
@Johann, якщо ви шукаєте чистої швидкості, то алгоритм Зігората загальновизнаний як найшвидший підхід. Крім того, вищезазначений підхід можна зробити швидше, переносячи значення від одного дзвінка до іншого.
Дрю Ноакс

Привіт, для чого слід встановити stdDevзмінну? Я розумію, що це може бути налаштовано на конкретні вимоги, але чи є межі (тобто значення max / min)?
hofnarwillie

@hofnarwillie stdDev - параметр масштабу нормального розподілу, який може бути будь-яким додатним числом. Чим вона більша, тим більше розсіюються генеровані числа. Для стандартного нормального розподілу використовуйте параметри mean = 0 і stdDev = 1.
yoyoyoyosef

1
@Jack Я так не думаю. Лише -2 * Math.Log (u1) знаходиться всередині sqrt, і журнал завжди буде від'ємним або нульовим, оскільки u1 <= 1
yoyoyoyoseose

63

Здається, це питання перемістилося поверх Google для .NET Gaussian поколення, тому я зрозумів, що опублікую відповідь.

Я створив кілька методів розширення для класу .NET Random , включаючи реалізацію перетворення Box-Muller. Оскільки вони є розширеннями, якщо проект включений (або ви посилаєтесь на складену DLL), ви все одно можете це зробити

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

Сподіваюсь, ніхто не заперечує безсоромної пробки

Зразок гістограми результатів (включена демонстраційна програма для малювання):

введіть тут опис зображення


У вашому класі розширення є кілька речей, які я шукав! Дякую!
Томас

1
у вас є невелика помилка у вашому методі NextGaussian. NextDouble () Повертає випадкове число з плаваючою комою, яке більше або дорівнює 0,0 та менше 1,0. Тож у вас повинно бути u1 = 1.0 - NextDouble () .... інший журнал (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


Чудова відповідь! Ця функція доступна в NuGet в пакеті MathNet.Numerics . Завжди здорово, щоб не довелося катати своє.
jpmc26

8

Я створив запит на таку функцію в Microsoft Connect. Якщо це щось, що ви шукаєте, будь ласка, проголосуйте за нього та підвищіть його видимість.

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

Ця функція включена в Java SDK. Її реалізація доступна як частина документації та легко переноситься на C # або інші мови .NET.

Якщо ви шукаєте чистої швидкості, то алгоритм Зігората загальновизнаний як найшвидший підхід.

Я не фахівець з цієї теми - я зіткнувся з потребою в цьому, впровадивши фільтр частинок для моєї роботизованої футбольної бібліотеки RoboCup 3D і здивувався, коли це не було включено в рамки.


Тим часом, ось обгортка для Randomцього забезпечує ефективну реалізацію полярного методу 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;
    }
}

Хоча я отримав від нього кілька значень. може хтось перевірити, що не так?
mk7

@ mk7, функція ймовірності Гаусса, орієнтована навколо нуля, так само ймовірна, щоб дати негативні значення, як і дати позитивні значення.
Дрю Ноакс

Ти маєш рацію! Оскільки я хотів би отримати список ваги в типовій популяції з гауссовим PDF, я встановлюю mu, скажімо, 75 [в кг], а сигму до 10. Чи потрібно мені встановити новий екземпляр GaussianRandom для генерації кожна випадкова вага?
mk7

Ви можете зберігати зразки малювання з одного примірника.
Дрю Ноакс


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

Важливо: Виберіть інтервал y та параметри σ і μ, щоб крива функції не була відсічена у максимальних / мінімальних точках (наприклад, при х = середнє значення). Подумайте про інтервали x і y як обмежувальну коробку, в яку повинна вміститися крива.


4
Tangenial, але це насправді перший раз, коли я зрозумів, що ви можете використовувати символи Unicode для змінних замість чогось німого, як _sigma або _phi ...
Slothario

@Slothario Я дякую розробникам скрізь за те, що вони використовували "щось німе": |
користувач2864740

2

Я хотів би розширити відповідь @ yoyoyoyoseosef, зробивши це ще швидше і написавши обгортковий клас. Натрачені накладні витрати можуть не означати вдвічі швидшого, але я думаю, що це має бути майже вдвічі швидше. Це не безпечно для потоків.

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, я також застосував клас "Гаусса", але для спрощення простору пам'яті я зробив це дочірнім класом Random, щоб ви могли також викликати базові Next (), NextDouble () , і т. д. з класу Гаусса, без необхідності створювати додатковий об'єкт Random для обробки. Я також усунув властивості глобального класу _available і _nextgauss, оскільки не вважав їх необхідними, оскільки цей клас заснований на екземплярі, він повинен бути безпечним для потоків, якщо ви даєте кожному потоку свій власний об'єкт Гаусса. Я також перемістив усі функції, виділені на час виконання, переміщені з функції та зробив їх властивостями класу, це зменшить кількість викликів до менеджера пам'яті, оскільки теоретично 4 дублювання ніколи не повинні бути виділені, поки об'єкт не буде знищений.

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

Місцеві змінні теж є друзями.
користувач2864740

1

Розширюючи відповідь Дрю Ноакса, якщо вам потрібна краща продуктивність, ніж Box-Muller (приблизно на 50-75% швидше), Колін Грін поділився реалізацією алгоритму Ziggurat в C #, який ви можете знайти тут:

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

Ziggurat використовує таблицю пошуку для обробки значень, що падають досить далеко від кривої, які вона швидко прийме або відхилить. Близько 2,5% часу він повинен робити подальші обчислення, щоб визначити, на якій стороні кривої знаходиться число.


0

Ви можете спробувати Infer.NET. Це все ще не комерційна ліцензія. Ось тут посилання

Це вірогідна основа для розробки .NET, розробленого нами. У них є .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. Ви можете збільшити роздільну здатність відповідно до своїх потреб. Це дуже швидко, просто і працює для моїх додатків нейронної мережі, яким для виконання завдання потрібен приблизний тип гауссового типу щільності ймовірності. Сподіваюся, це допоможе комусь заощадити час та цикли процесора. Хоча це для мене чудово працює, це обмежене наближення діапазону, тож пам’ятайте, що хвости закриті та обмежені, але, безумовно, ви можете їх розширити за потреби.
Даніель Говард

1
Ей, Даніелю, я запропонував редагувати, що включає опис вашого коментаря до самої відповіді. Він також видаляє '//', котрий коментував реальний код у вашій відповіді. Ви можете зробити редагування самостійно, якщо хочете / якщо його буде відхилено :)
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 на регулярній основі?
Девід Арно

3
Можливо; що я кажу, що це набагато більш спеціалізовано. У ньому є лише одна веб-програма для використання. Гауссові дистрибуції мають неймовірну кількість споріднених застосувань.
TraumaPony

@DavidArno, ви серйозно пропонуєте, що менша функціональність покращує рамки.
Джодрелл

1
@Jodrell, щоб навести конкретний приклад, я думаю, що рішення про те, щоб зробити MVC окремим фреймворком, а не частиною основної .NET рамки, було вдалим.
Девід Арно
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.