Случайные гауссовы переменные
-
03-07-2019 - |
Вопрос
Есть ли класс в стандартной библиотеке .NET, который дает мне функциональность для создания случайных величин, которые следуют гауссовскому распределению?
Решение
Предложение Джарретта об использовании преобразования Бокса-Мюллера хорошо для быстрого и грязного решения. Простая реализация:
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)
Другие советы
Похоже, что этот вопрос переместился на вершину Google для поколения .NET Gaussian, поэтому я решил опубликовать ответ.
Я создал несколько методов расширения для класса .NET Random , включая реализацию преобразование Бокса-Мюллера. Поскольку они являются расширениями, если проект включен (или вы ссылаетесь на скомпилированную DLL), вы все равно можете делать
var r = new Random();
var x = r.NextGaussian();
Надеюсь, никто не возражает против бесстыдной вилки.
Пример гистограммы результатов (демонстрационное приложение для рисования включено):
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 а> р>
Я создал запрос на такую функцию в Microsoft Connect.Если это то, что вы ищете, пожалуйста, проголосуйте за это и увеличьте его видимость.
Эта функция включена в Java SDK.Его реализация доступна как часть документации и легко портируется на C # или другие языки .NET.
Если вы ищете чистую скорость, то Алгоритм Зигората общепризнано, что это самый быстрый подход.
Однако я не эксперт в этой теме - я столкнулся с необходимостью этого при реализации фильтр для твердых частиц для моего RoboCup 3D имитированная роботизированная футбольная библиотека и был удивлен, когда это не было включено в рамки.
А пока вот обертка для 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;
}
}
Math.NET Iridium также заявляет о реализации "неоднородных случайных генераторов (нормальных, пуассоновых, биномиальных) , ...) ".
Вот еще одно быстрое и грязное решение для генерации случайных величин, которые являются нормально распределенный.Он рисует некоторую случайную точку (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 как ограничивающий прямоугольник, в который должна вписываться кривая.
Я хотел бы расширить ответ @ 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();
}
}
Расширяя ответ Дрю Ноакса, если вам нужна лучшая производительность, чем у Бокса-Мюллера (примерно на 50-75% быстрее), Колин Грин поделился реализацией алгоритма Зиккурата в C #, который вы можете найти здесь:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
Ziggurat использует справочную таблицу для обработки значений, которые находятся достаточно далеко от кривой, которые он быстро примет или отклонит. Примерно в 2,5% случаев приходится выполнять дальнейшие вычисления, чтобы определить, на какой стороне кривой находится число.
Расширяя ответы @Noakes и @ Hameer, я также реализовал класс 'Gaussian', но чтобы упростить пространство памяти, я сделал его дочерним по отношению к классу 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));
}
}
Вы можете попробовать Infer.NET. Это еще не коммерческая лицензия, хотя. Вот ссылка
Это вероятностная структура для .NET, разработанная моими исследованиями Microsoft. У них есть .NET-типы для дистрибутивов Бернулли, Бета, Гамма, Гаусса, Пуассона и, возможно, еще некоторых, которые я пропустил.
Это может выполнить то, что вы хотите. Спасибо.
Это моя простая реализация, вдохновленная 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;
}
}
Я не думаю, что есть. И я действительно надеюсь, что это не так, потому что фреймворк уже достаточно раздутый, без такой специализированной функциональности, которая заполняет его еще больше.
Посмотрите на http://www.extremeoptimization.com/Statistics /UsersGuide/ContinuousDistributions/NormalDistribution.aspx и http: //www.vbforums .com / showthread.php? t = 488959 для сторонних .NET-решений.