Как я могу преобразовать равномерное распределение (как и большинство генераторов случайных чисел, например, между 0,0 и 1,0) в нормальное распределение? Что, если я хочу выбрать среднее значение и стандартное отклонение?
Как я могу преобразовать равномерное распределение (как и большинство генераторов случайных чисел, например, между 0,0 и 1,0) в нормальное распределение? Что, если я хочу выбрать среднее значение и стандартное отклонение?
Ответы:
Алгоритм Зиккурат довольно эффективен для этого, хотя преобразование Бокса-Мюллера проще реализовать с нуля (а не сумасшедший медленно).
Есть много способов:
Изменение распределения одной функции на другую предполагает использование функции, обратной функции, которую вы хотите.
Другими словами, если вы стремитесь к определенной функции вероятности p (x), вы получаете распределение, интегрируя по ней -> d (x) = интеграл (p (x)) и используя его обратное: Inv (d (x)) . Теперь используйте функцию случайной вероятности (которая имеет равномерное распределение) и передайте значение результата через функцию Inv (d (x)). Вы должны получить случайные значения с распределением в соответствии с выбранной вами функцией.
Это общий математический подход - с его помощью теперь вы можете выбрать любую имеющуюся функцию вероятности или распределения, если она имеет обратное или хорошее обратное приближение.
Надеюсь, это помогло, и спасибо за небольшое замечание об использовании распределения, а не самой вероятности.
Вот реализация javascript с использованием полярной формы преобразования Бокса-Мюллера.
/*
* Returns member of set with a given mean and standard deviation
* mean: mean
* standard deviation: std_dev
*/
function createMemberInNormalDistribution(mean,std_dev){
return mean + (gaussRandom()*std_dev);
}
/*
* Returns random number in normal distribution centering on 0.
* ~95% of numbers returned should fall between -2 and 2
* ie within two standard deviations
*/
function gaussRandom() {
var u = 2*Math.random()-1;
var v = 2*Math.random()-1;
var r = u*u + v*v;
/*if outside interval [0,1] start over*/
if(r == 0 || r >= 1) return gaussRandom();
var c = Math.sqrt(-2*Math.log(r)/r);
return u*c;
/* todo: optimize this algorithm by caching (v*c)
* and returning next time gaussRandom() is called.
* left out for simplicity */
}
Используйте центральную предельную теорему wikipedia entry mathworld entry в своих интересах.
Сгенерируйте n из равномерно распределенных чисел, просуммируйте их, вычтите n * 0,5, и вы получите результат примерно нормального распределения со средним значением, равным 0, и дисперсией, равной (1/12) * (1/sqrt(N))
(см. Википедию о равномерном распределении для последнего)
n = 10 дает что-то наполовину приличное быстро. Если вам нужно что-то более чем наполовину приличное, выберите решение Tylers (как указано в статье в Википедии о нормальных дистрибутивах )
Я бы использовал Box-Muller. Об этом две вещи:
Где R1, R2 - случайные равномерные числа:
НОРМАЛЬНОЕ РАСПРЕДЕЛЕНИЕ с SD равным 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)
Это точно ... нет необходимости делать все эти медленные петли!
Кажется невероятным, что я мог бы что-то добавить к этому через восемь лет, но в случае Java я хотел бы указать читателям на метод Random.nextGaussian () , который генерирует для вас гауссово распределение со средним значением 0,0 и стандартным отклонением 1,0.
Простое сложение и / или умножение изменит среднее значение и стандартное отклонение в соответствии с вашими потребностями.
Стандартный модуль библиотеки Python random имеет то, что вы хотите:
normalvariate (mu, sigma)
Нормальное распределение. mu - среднее значение, а сигма - стандартное отклонение.
Что касается самого алгоритма, взгляните на функцию в random.py в библиотеке Python.
Это моя реализация на JavaScript алгоритма P ( полярный метод для нормальных отклонений ) из раздела 3.4.1 книги Дональда Кнута Искусство компьютерного программирования :
function normal_random(mean,stddev)
{
var V1
var V2
var S
do{
var U1 = Math.random() // return uniform distributed in [0,1[
var U2 = Math.random()
V1 = 2*U1-1
V2 = 2*U2-1
S = V1*V1+V2*V2
}while(S >= 1)
if(S===0) return 0
return mean+stddev*(V1*Math.sqrt(-2*Math.log(S)/S))
}
Я считаю, что вам следует попробовать это в EXCEL : =norminv(rand();0;1)
. Это произведет произведение случайных чисел, которые должны быть нормально распределены с нулевым средним и объединенной дисперсией. «0» может быть поставлен с любым значением, так что числа будут иметь желаемое среднее, и, изменив «1», вы получите дисперсию, равную квадрату вашего ввода.
Например: =norminv(rand();50;3)
будет соответствовать нормально распределенным числам со СРЕДНИМ = 50 РАЗБИРАТЕЛЬНОСТЬ = 9.
В Как я могу преобразовать равномерное распределение (как и большинство генераторов случайных чисел, например, между 0,0 и 1,0) в нормальное распределение?
Для программной реализации я знаю пару имен генераторов случайных чисел, которые дают вам псевдооднородную случайную последовательность в [0,1] (Mersenne Twister, Linear Congruate Generator). Назовем это U (x)
Существует математическая область, которая называется теорией вероятностей. Первое: если вы хотите смоделировать rv с интегральным распределением F, вы можете попробовать просто оценить F ^ -1 (U (x)). В пр. Теории было доказано, что такая с.в. будет иметь интегральное распределение F.
Шаг 2 может применяться для генерации rv ~ F без использования каких-либо методов подсчета, когда F ^ -1 может быть получено аналитически без проблем. (например, Exp.distribution)
Чтобы смоделировать нормальное распределение, вы можете вычислить y1 * cos (y2), где y1 ~ равномерно в [0,2pi]. y2 - распределение релей.
В: Что, если мне нужно выбрать среднее и стандартное отклонение по моему выбору?
Вы можете вычислить сигму * N (0,1) + m.
Можно показать, что такое смещение и масштабирование приводят к N (m, сигма)
Это реализация Matlab с использованием полярной формы преобразования Бокса-Мюллера :
Функция randn_box_muller.m
:
function [values] = randn_box_muller(n, mean, std_dev)
if nargin == 1
mean = 0;
std_dev = 1;
end
r = gaussRandomN(n);
values = r.*std_dev - mean;
end
function [values] = gaussRandomN(n)
[u, v, r] = gaussRandomNValid(n);
c = sqrt(-2*log(r)./r);
values = u.*c;
end
function [u, v, r] = gaussRandomNValid(n)
r = zeros(n, 1);
u = zeros(n, 1);
v = zeros(n, 1);
filter = r==0 | r>=1;
% if outside interval [0,1] start over
while n ~= 0
u(filter) = 2*rand(n, 1)-1;
v(filter) = 2*rand(n, 1)-1;
r(filter) = u(filter).*u(filter) + v(filter).*v(filter);
filter = r==0 | r>=1;
n = size(r(filter),1);
end
end
И ссылаясь на histfit(randn_box_muller(10000000),100);
это результат:
Очевидно, это действительно неэффективно по сравнению со встроенным в Matlab randn .
У меня есть следующий код, который может помочь:
set.seed(123)
n <- 1000
u <- runif(n) #creates U
x <- -log(u)
y <- runif(n, max=u*sqrt((2*exp(1))/pi)) #create Y
z <- ifelse (y < dnorm(x)/2, -x, NA)
z <- ifelse ((y > dnorm(x)/2) & (y < dnorm(x)), x, z)
z <- z[!is.na(z)]
Также проще использовать реализованную функцию rnorm (), поскольку она быстрее, чем написание генератора случайных чисел для нормального распределения. См. Следующий код как доказательство
n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0
function distRandom(){
do{
x=random(DISTRIBUTION_DOMAIN);
}while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
return x;
}