Есть ли класс в стандартной библиотеке .NET, который дает мне возможность создавать случайные переменные, которые следуют за распределением Гаусса?Случайные гауссовские переменные
ответ
Предложение Джарретта об использовании преобразования 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)
не думаю есть. И я действительно надеюсь, что нет, поскольку структура уже достаточно раздута, без такой специализированной функциональности, наполняющей ее еще больше.
Посмотрите на http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx и http://www.vbforums.com/showthread.php?t=488959 для сторонних .NET-решений.
С каких пор гауссовское распределение «специализировано»? Это гораздо более общий, чем, скажем, AJAX или DataTables. – TraumaPony 2008-10-20 14:49:57
@TraumaPony: вы серьезно пытаетесь предложить разработчикам больше использовать распределение Гаусса, чем использовать AJAX на регулярной основе? – 2008-10-20 16:53:01
Возможно; я говорю, что он гораздо более специализирован. В нем есть только одно приложение - веб-приложения. Гауссовские распределения имеют невероятное количество несвязанных применений. – TraumaPony 2008-10-21 02:34:51
http://mathworld.wolfram.com/Box-MullerTransformation.html
С помощью двух случайных величин, вы можете генерировать случайные значения по распределению Гаусса. Это не сложная задача.
Math.NET Iridium также утверждает, что для реализации "неоднородных случайных генераторов (нормальные, пуассоновские, биномиальный, ...)".
Я создал запрос на такую функцию в Microsoft Connect. Если это то, что вы ищете, проголосуйте за него и повысите его видимость.
Эта функция включена в Java SDK. Его реализация доступна as part of the documentation и легко переносится на C# или другие языки .NET.
Если вы ищете чистую скорость, то Zigorat Algorithm обычно считается самым быстрым подходом.
Я не специалист по этой теме, хотя - я столкнулся с необходимостью этого при реализации particle filter для моего RoboCup 3D simulated robotic soccer library и был удивлен, когда это не было включено в структуру.
В то же время, вот обертка для 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;
}
}
Вы можете попробовать Infer.NET. Тем не менее, это не коммерческая лицензия. Вот link
Это вероятностная структура для .NET, разработанная моим исследованием Microsoft. У них есть типы .NET для распределений Бернулли, Бета, Гамма, Гаусса, Пуассона и, возможно, еще кое-что, что я забыл.
Он может выполнить то, что вы хотите. Благодарю.
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
Этот вопрос переместились на вершине Google для .NET поколения гауссова, поэтому я решил опубликовать ответ.
Я сделал несколько extension methods for the .NET Random class, включая реализацию преобразования Box-Muller. Так как они расширения, до тех пор, как проект включен (или ссылку на скомпилированный DLL), вы все еще можете сделать
var r = new Random();
var x = r.NextGaussian();
Надежда Никто умам бесстыдный штепсель.
Пример гистограммы результатов (демо-приложение для рисования это включено):
Я хотел бы расширить на @ 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();
}
}
Это моя простая вставка 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-Мюллера (около 50-75% быстрее), Колин Грин разделил реализацию алгоритма Ziggurat в C#, который вы можете найти здесь:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
Зиггурат использует таблицу поиска для обработки значений, которые падают достаточно далеко от кривой, он будет быстро принять или отклонить. Примерно в 2,5% времени он должен выполнить дальнейшие вычисления, чтобы определить, на какой стороне кривой находится число.
Вот еще одно быстрое и грязное решение для генерации случайных величин, которые являются normal distributed. Он рисует некоторую случайную точку (x, y) и проверяет, находится ли эта точка под кривой вашей функции плотности вероятности, в противном случае повторяется.
Бонус: вы можете генерировать случайные переменные для любого другого распределения (например, exponential distribution или poisson distribution), просто заменив функцию плотности.
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 * σ * σ));
}
Важно: Выберите интервал у и параметры σ и μ так, чтобы кривая функции не отсечки на это максимальные/минимальные точки (например, при х = среднее). Подумайте интервалов х и у как ограничивающий прямоугольник, в котором кривая должна вписываться в.
Расширение прочь @Noakes и @ ответов Хамеер, я также реализовал класс «гауссовой», но чтобы упростить пространство памяти, я сделал его дочерним элементом класса 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));
}
}
Я хотел бы просто добавить математический результат, который не сразу полезен для нормальных распределений (из-за сложного CDF), но полезен для многих других дистрибутивов. Если вы помещаете равномерно распределенные случайные числа в [0,1] (с «Random.NextDouble()») в обратную сторону CDF ЛЮБОГО распространения, вы получите случайные числа, которые следуют за распределением THAT. Если вашему приложению не нужны точно распределенные переменные, то распределение логистики является очень близким приближением к нормальному и имеет легко обратимый CDF. – Ozzah 2012-11-22 23:53:53
[MedallionRandom NuGet package] (https://github.com/madelson/MedallionUtilities/tree/master/MedallionRandom) содержит метод расширения для извлечения нормально распределенных значений из «Случайного» с использованием преобразования Box-Muller (упомянутое в несколько ответов ниже). – ChaseMedallion 2016-05-07 14:35:18