Я просто хотел бы добавить математический результат, который не сразу полезен для нормальных распределений (из-за сложной CDF), но полезен для многих других распределений. Если вы поместите равномерно распределенные случайные числа в [0,1] (с Random.NextDouble()) в инверсию CDF ЛЮБОГО распределения, вы получите случайные числа, которые соответствуют ЭТОМ распределению. Если вашему приложению не нужны точно нормально распределенные переменные, тогда логистическое распределение очень близко к нормальному и имеет легко обратимый CDF.
Ozzah
1
Пакет NuGet MedallionRandom содержит метод расширения для получения нормально распределенных значений из объекта с Randomиспользованием преобразования Бокса-Мюллера (упомянутого в нескольких ответах ниже).
ChaseMedallion
Ответы:
182
Предложение Джарретта об использовании преобразования Бокса-Мюллера хорошо для быстрого и грязного решения. Простая реализация:
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)
Я протестировал его и сравнил с 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 =newRandom();var x = r.NextGaussian();
Надеюсь, никто не против бесстыдной пробки.
Пример гистограммы результатов (прилагается демонстрационное приложение для ее рисования):
В вашем классе расширения есть кое-что, что я искал! Спасибо!
Thomas
1
у вас есть небольшая ошибка в вашем методе NextGaussian. NextDouble () Возвращает случайное число с плавающей запятой, которое больше или равно 0,0, но меньше 1,0. Итак, у вас должно быть u1 = 1.0 - NextDouble () .... другой журнал (0) взорвется
А пока вот оболочка, Randomкоторая обеспечивает эффективную реализацию полярного метода Бокса Мюллера:
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;}}
Хотя я получил некоторые значения -ve. может кто нибудь проверить что не так?
mk7
@ mk7, гауссова функция вероятности с центром вокруг нуля с такой же вероятностью даст отрицательные значения, как и положительные.
Дрю Ноукс
Ты прав! Так как я хотел бы получить список весов в типичной популяции с гауссовским PDF, я устанавливаю mu, скажем, 75 [в кг], а sigma равным 10. Нужно ли мне устанавливать новый экземпляр GaussianRandom для генерации каждый случайный вес?
mk7
Вы можете продолжать рисовать образцы из одного экземпляра.
Дрю Ноукс
5
Math.NET Iridium также утверждает, что реализует «неоднородные генераторы случайных чисел (нормальные, пуассоновские, биномиальные, ...)».
Но он не работает должным образом. Пытался это заговорить, давая единый случайный нет.
Nikhil Chilwant
4
Вот еще одно быстрое и грязное решение для генерации случайных величин с нормальным распределением . Он рисует некоторую случайную точку (x, y) и проверяет, находится ли эта точка под кривой вашей функции плотности вероятности, в противном случае повторите.
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*σ*σ));}
Важно: выберите интервал y и параметры σ и μ, чтобы кривая функции не была обрезанной в точках максимума / минимума (например, при x = среднее). Подумайте об интервалах x и y как об ограничивающем прямоугольнике, в который должна вписаться кривая.
Tangenial, но на самом деле это первый раз, когда я понял, что вы можете использовать символы Unicode для переменных вместо чего-то тупого типа _sigma или _phi ...
Slothario
@Slothario Я благодарю разработчиков во всем мире за то, что они использовали «что-то глупое»: |
user2864740 09
2
Я хотел бы расширить ответ @ yoyoyoyosef, сделав его еще быстрее и написав класс-оболочку. Накладные расходы не могут означать, что в два раза быстрее, но я думаю, что они должны быть почти в два раза быстрее. Однако это не является потокобезопасным.
Расширяя ответы @Noakes и @ Hameer, я также реализовал класс 'Gaussian', но для упрощения пространства памяти я сделал его дочерним по отношению к классу Random, чтобы вы также могли вызывать базовые функции Next (), NextDouble () и т. д. из класса Gaussian без необходимости создавать дополнительный объект Random для его обработки. Я также исключил свойства глобального класса _available и _nextgauss, поскольку я не видел в них необходимости, поскольку этот класс основан на экземплярах, он должен быть потокобезопасным, если вы дадите каждому потоку свой собственный гауссовский объект. Я также переместил все переменные, выделенные во время выполнения, из функции и сделал их свойствами класса, это уменьшит количество вызовов диспетчера памяти, поскольку 4 двойных числа теоретически никогда не должны деактивироваться, пока объект не будет уничтожен.
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));}}
Расширяя ответ Дрю Ноукса, если вам нужна более высокая производительность, чем у Box-Muller (примерно на 50-75% быстрее), Колин Грин поделился реализацией алгоритма Ziggurat на C #, которую вы можете найти здесь:
Ziggurat использует справочную таблицу для обработки значений, которые падают достаточно далеко от кривой, которые он быстро принимает или отклоняет. Примерно в 2,5% случаев ему приходится выполнять дальнейшие вычисления, чтобы определить, на какой стороне кривой находится число.
Вы можете попробовать Infer.NET. Однако это еще не коммерческая лицензия. Вот там ссылка
Это вероятностная структура для .NET, разработанная в моем исследовании Microsoft. У них есть типы .NET для распределений Бернулли, Бета, Гамма, Гаусса, Пуассона и, вероятно, еще некоторые, которые я не упомянул.
Это моя простая реализация, вдохновленная Боксом Мюллером. Вы можете увеличить разрешение по своему усмотрению. Хотя это отлично работает для меня, это приближение ограниченного диапазона, поэтому имейте в виду, что хвосты закрытые и конечные, но, конечно, вы можете расширять их по мере необходимости.
Это моя простая реализация, вдохновленная Боксом Мюллером. Вы можете увеличить разрешение по своему усмотрению. Это очень быстро, просто и работает для моих нейросетевых приложений, которым для выполнения работы требуется приблизительный гауссовский тип функции плотности вероятности. Надеюсь, это поможет кому-то сэкономить время и циклы процессора. Хотя это отлично работает для меня, это приближение ограниченного диапазона, поэтому имейте в виду, что хвосты закрытые и конечные, но, конечно, вы можете расширять их по мере необходимости.
Дэниел Ховард,
1
Привет, Даниэль, я предложил отредактировать, чтобы включить описание из вашего комментария в сам ответ. Он также удаляет "//", который комментировал реальный код в вашем ответе. Вы можете сделать редактирование самостоятельно, если хотите / если оно будет отклонено :)
mbrig
-1
Я не думаю, что есть. И я очень надеюсь, что это не так, поскольку фреймворк уже достаточно раздут, без такой специализированной функциональности, которая еще больше наполняет его.
С каких это пор гауссово распределение стало «специализированным»? Это гораздо более общий подход, чем, скажем, AJAX или DataTables.
TraumaPony
@TraumaPony: вы серьезно пытаетесь предложить большему количеству разработчиков использовать распределение Гаусса, чем использовать AJAX на регулярной основе?
Дэвид Арно,
3
Возможно; Я хочу сказать, что он гораздо более специализированный. У него только одно использование - веб-приложения. Гауссовские распределения имеют невероятное количество не связанных между собой применений.
TraumaPony
@DavidArno, вы серьезно предлагаете, чтобы меньшая функциональность улучшала структуру.
Джодрелл
1
@Jodrell, чтобы процитировать конкретный пример, я думаю, что решение сделать MVC отдельной структурой, а не частью основной платформы .NET, было хорошим.
Random.NextDouble()
) в инверсию CDF ЛЮБОГО распределения, вы получите случайные числа, которые соответствуют ЭТОМ распределению. Если вашему приложению не нужны точно нормально распределенные переменные, тогда логистическое распределение очень близко к нормальному и имеет легко обратимый CDF.Random
использованием преобразования Бокса-Мюллера (упомянутого в нескольких ответах ниже).Ответы:
Предложение Джарретта об использовании преобразования Бокса-Мюллера хорошо для быстрого и грязного решения. Простая реализация:
источник
stdDev
установить для переменной? Я понимаю, что это можно настроить в соответствии с конкретными требованиями, но есть ли какие-либо границы (например, значения max / min)?Похоже, этот вопрос переместился на вершину Google для поколения .NET Gaussian, поэтому я решил, что опубликую ответ.
Я сделал несколько методов расширения для класса .NET Random , включая реализацию преобразования Бокса-Мюллера. Поскольку они являются расширениями, пока проект включен (или вы ссылаетесь на скомпилированную DLL), вы все равно можете делать
Надеюсь, никто не против бесстыдной пробки.
Пример гистограммы результатов (прилагается демонстрационное приложение для ее рисования):
источник
Math.NET предоставляет эту функцию. Вот как:
Вы можете найти документацию здесь: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm
источник
Я создал запрос на такую функцию в Microsoft Connect. Если это то, что вы ищете, проголосуйте за это и увеличьте его видимость.
https://connect.microsoft.com/VisualStudio/feedback/details/634346/guassian-normal-distribution-random-numbers
Эта функция включена в Java SDK. Его реализация доступна как часть документации и легко переносится на C # или другие языки .NET.
Если вы ищете чистую скорость, то алгоритм Зигората общепризнан как самый быстрый подход.
Я не эксперт в этой теме - я столкнулся с необходимостью в этом при реализации фильтра частиц для моей 3D-библиотеки роботов-футбольных роботов RoboCup и был удивлен, когда это не было включено в структуру.
А пока вот оболочка,
Random
которая обеспечивает эффективную реализацию полярного метода Бокса Мюллера:источник
Math.NET Iridium также утверждает, что реализует «неоднородные генераторы случайных чисел (нормальные, пуассоновские, биномиальные, ...)».
источник
Вот еще одно быстрое и грязное решение для генерации случайных величин с нормальным распределением . Он рисует некоторую случайную точку (x, y) и проверяет, находится ли эта точка под кривой вашей функции плотности вероятности, в противном случае повторите.
Бонус: вы можете сгенерировать случайные величины для любого другого распределения (например, экспоненциального распределения или распределения Пуассона ), просто заменив функцию плотности.
Важно: выберите интервал y и параметры σ и μ, чтобы кривая функции не была обрезанной в точках максимума / минимума (например, при x = среднее). Подумайте об интервалах x и y как об ограничивающем прямоугольнике, в который должна вписаться кривая.
источник
Я хотел бы расширить ответ @ yoyoyoyosef, сделав его еще быстрее и написав класс-оболочку. Накладные расходы не могут означать, что в два раза быстрее, но я думаю, что они должны быть почти в два раза быстрее. Однако это не является потокобезопасным.
источник
Расширяя ответы @Noakes и @ Hameer, я также реализовал класс 'Gaussian', но для упрощения пространства памяти я сделал его дочерним по отношению к классу Random, чтобы вы также могли вызывать базовые функции Next (), NextDouble () и т. д. из класса Gaussian без необходимости создавать дополнительный объект Random для его обработки. Я также исключил свойства глобального класса _available и _nextgauss, поскольку я не видел в них необходимости, поскольку этот класс основан на экземплярах, он должен быть потокобезопасным, если вы дадите каждому потоку свой собственный гауссовский объект. Я также переместил все переменные, выделенные во время выполнения, из функции и сделал их свойствами класса, это уменьшит количество вызовов диспетчера памяти, поскольку 4 двойных числа теоретически никогда не должны деактивироваться, пока объект не будет уничтожен.
источник
Расширяя ответ Дрю Ноукса, если вам нужна более высокая производительность, чем у Box-Muller (примерно на 50-75% быстрее), Колин Грин поделился реализацией алгоритма Ziggurat на C #, которую вы можете найти здесь:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
Ziggurat использует справочную таблицу для обработки значений, которые падают достаточно далеко от кривой, которые он быстро принимает или отклоняет. Примерно в 2,5% случаев ему приходится выполнять дальнейшие вычисления, чтобы определить, на какой стороне кривой находится число.
источник
Вы можете попробовать Infer.NET. Однако это еще не коммерческая лицензия. Вот там ссылка
Это вероятностная структура для .NET, разработанная в моем исследовании Microsoft. У них есть типы .NET для распределений Бернулли, Бета, Гамма, Гаусса, Пуассона и, вероятно, еще некоторые, которые я не упомянул.
Это может сделать то, что вы хотите. Спасибо.
источник
Это моя простая реализация, вдохновленная Боксом Мюллером. Вы можете увеличить разрешение по своему усмотрению. Хотя это отлично работает для меня, это приближение ограниченного диапазона, поэтому имейте в виду, что хвосты закрытые и конечные, но, конечно, вы можете расширять их по мере необходимости.
источник
Я не думаю, что есть. И я очень надеюсь, что это не так, поскольку фреймворк уже достаточно раздут, без такой специализированной функциональности, которая еще больше наполняет его.
Взгляните на http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx и http://www.vbforums.com/showthread.php?t=488959 для сторонних решений .NET.
источник