Случайные гауссовские переменные


118

Есть ли в стандартной библиотеке .NET класс, который дает мне возможность создавать случайные переменные, соответствующие распределению Гаусса?


http://mathworld.wolfram.com/Box-MullerTransformation.html Используя две случайные величины, вы можете генерировать случайные значения в соответствии с распределением Гаусса. Это совсем не сложная задача.
Джарретт Мейер,

1
Я просто хотел бы добавить математический результат, который не сразу полезен для нормальных распределений (из-за сложной CDF), но полезен для многих других распределений. Если вы поместите равномерно распределенные случайные числа в [0,1] (с Random.NextDouble()) в инверсию CDF ЛЮБОГО распределения, вы получите случайные числа, которые соответствуют ЭТОМ распределению. Если вашему приложению не нужны точно нормально распределенные переменные, тогда логистическое распределение очень близко к нормальному и имеет легко обратимый CDF.
Ozzah

1
Пакет NuGet MedallionRandom содержит метод расширения для получения нормально распределенных значений из объекта с Randomиспользованием преобразования Бокса-Мюллера (упомянутого в нескольких ответах ниже).
ChaseMedallion

Ответы:


182

Предложение Джарретта об использовании преобразования Бокса-Мюллера хорошо для быстрого и грязного решения. Простая реализация:

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. Ваша версия более чем в два раза быстрее, и конечный результат в основном такой же (визуальный осмотр «колокольчиков»).
Иоганн Герелл,

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

Привет, что нужно stdDevустановить для переменной? Я понимаю, что это можно настроить в соответствии с конкретными требованиями, но есть ли какие-либо границы (например, значения max / min)?
hofnarwillie

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

1
@ Джек, я так не думаю. Только -2 * Math.Log (u1) находится внутри sqrt, и журнал всегда будет отрицательным или нулевым, поскольку u1 <= 1
yoyoyoyosef

63

Похоже, этот вопрос переместился на вершину Google для поколения .NET Gaussian, поэтому я решил, что опубликую ответ.

Я сделал несколько методов расширения для класса .NET Random , включая реализацию преобразования Бокса-Мюллера. Поскольку они являются расширениями, пока проект включен (или вы ссылаетесь на скомпилированную DLL), вы все равно можете делать

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

Надеюсь, никто не против бесстыдной пробки.

Пример гистограммы результатов (прилагается демонстрационное приложение для ее рисования):

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


В вашем классе расширения есть кое-что, что я искал! Спасибо!
Thomas

1
у вас есть небольшая ошибка в вашем методе NextGaussian. NextDouble () Возвращает случайное число с плавающей запятой, которое больше или равно 0,0, но меньше 1,0. Итак, у вас должно быть u1 = 1.0 - NextDouble () .... другой журнал (0) взорвется
Митч Уит

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-numbers

Эта функция включена в Java SDK. Его реализация доступна как часть документации и легко переносится на C # или другие языки .NET.

Если вы ищете чистую скорость, то алгоритм Зигората общепризнан как самый быстрый подход.

Я не эксперт в этой теме - я столкнулся с необходимостью в этом при реализации фильтра частиц для моей 3D-библиотеки роботов-футбольных роботов RoboCup и был удивлен, когда это не было включено в структуру.


А пока вот оболочка, Randomкоторая обеспечивает эффективную реализацию полярного метода Бокса Мюллера:

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, гауссова функция вероятности с центром вокруг нуля с такой же вероятностью даст отрицательные значения, как и положительные.
Дрю Ноукс

Ты прав! Так как я хотел бы получить список весов в типичной популяции с гауссовским PDF, я устанавливаю mu, скажем, 75 [в кг], а sigma равным 10. Нужно ли мне устанавливать новый экземпляр GaussianRandom для генерации каждый случайный вес?
mk7

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

5

Math.NET Iridium также утверждает, что реализует «неоднородные генераторы случайных чисел (нормальные, пуассоновские, биномиальные, ...)».


Но он не работает должным образом. Пытался это заговорить, давая единый случайный нет.
Nikhil Chilwant

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 = среднее). Подумайте об интервалах x и y как об ограничивающем прямоугольнике, в который должна вписаться кривая.


4
Tangenial, но на самом деле это первый раз, когда я понял, что вы можете использовать символы Unicode для переменных вместо чего-то тупого типа _sigma или _phi ...
Slothario

@Slothario Я благодарю разработчиков во всем мире за то, что они использовали «что-то глупое»: |
user2864740 09

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', но для упрощения пространства памяти я сделал его дочерним по отношению к классу Random, чтобы вы также могли вызывать базовые функции Next (), NextDouble () и т. д. из класса Gaussian без необходимости создавать дополнительный объект 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));
    }
}

Локальные переменные тоже друзья.
user2864740 09

1

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

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

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


0

Вы можете попробовать Infer.NET. Однако это еще не коммерческая лицензия. Вот там ссылка

Это вероятностная структура для .NET, разработанная в моем исследовании Microsoft. У них есть типы .NET для распределений Бернулли, Бета, Гамма, Гаусса, Пуассона и, вероятно, еще некоторые, которые я не упомянул.

Это может сделать то, что вы хотите. Спасибо.


0

Это моя простая реализация, вдохновленная Боксом Мюллером. Вы можете увеличить разрешение по своему усмотрению. Хотя это отлично работает для меня, это приближение ограниченного диапазона, поэтому имейте в виду, что хвосты закрытые и конечные, но, конечно, вы можете расширять их по мере необходимости.

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

Это моя простая реализация, вдохновленная Боксом Мюллером. Вы можете увеличить разрешение по своему усмотрению. Это очень быстро, просто и работает для моих нейросетевых приложений, которым для выполнения работы требуется приблизительный гауссовский тип функции плотности вероятности. Надеюсь, это поможет кому-то сэкономить время и циклы процессора. Хотя это отлично работает для меня, это приближение ограниченного диапазона, поэтому имейте в виду, что хвосты закрытые и конечные, но, конечно, вы можете расширять их по мере необходимости.
Дэниел Ховард,

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.