Як я можу перетворити рівномірний розподіл (як виробляє більшість генераторів випадкових чисел, наприклад, від 0,0 до 1,0) у звичайний розподіл? Що робити, якщо я хочу середнього та стандартного відхилення свого вибору?
Як я можу перетворити рівномірний розподіл (як виробляє більшість генераторів випадкових чисел, наприклад, від 0,0 до 1,0) у звичайний розподіл? Що робити, якщо я хочу середнього та стандартного відхилення свого вибору?
Відповіді:
Алгоритм Ziggurat досить ефективний для цього, хоча трансформацію Box-Muller легше здійснити з нуля (і не з розуму повільно).
Існує маса методів:
Зміна розподілу будь-якої функції на іншу передбачає використання зворотної функції, яку ви хочете.
Іншими словами, якщо ви орієнтуєтесь на певну функцію ймовірності p (x), ви отримуєте розподіл, інтегруючи над ним -> d (x) = інтеграл (p (x)) і використовуючи його зворотну: Inv (d (x)) . Тепер скористайтеся функцією випадкової ймовірності (які мають рівномірний розподіл) і переведіть значення результату через функцію Inv (d (x)). Ви повинні отримати випадкові значення, подані з розподілом, відповідно до обраної вами функції.
Це загальний математичний підхід - використовуючи його, ви тепер можете вибрати будь-яку функцію ймовірності чи розподілу, якщо у вас є зворотне або хороше зворотне наближення.
Сподіваюсь, це допомогло і дякую за невелике зауваження щодо використання розподілу, а не самої ймовірності.
Ось реалізація javascript з використанням полярної форми перетворення Box-Muller.
/*
* 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 */
}
Використовуйте центральну межу теореми вікіпедії для вступу mathworld в свою користь.
Створіть n з рівномірно розподілених чисел, підсумуйте їх, відніміть n * 0,5, і ви отримаєте приблизно нормальний розподіл із середнім рівним 0 та дисперсією, рівною (1/12) * (1/sqrt(N))
(див. Вікіпедію про рівномірні розподіли для останнього)
n = 10 дає вам щось наполовину пристойне швидко. Якщо ви хочете чогось більш ніж наполовину гідного, займіться рішенням стилів (як зазначено у статті Вікіпедії про звичайні дистрибуції )
Я б використовував Box-Muller. Про це дві речі:
Де R1, R2 - випадкові однакові числа:
НОРМАЛЬНЕ РОЗМІСТЕННЯ, з SD 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)
Це точно ... не потрібно робити усі ці повільні петлі!
Здається неймовірним, що я можу щось додати до цього через вісім років, але для випадку Java я хотів би вказати читачам на метод Random.nextGaussian () , який генерує розподіл Гаусса із середнім значенням 0,0 та стандартним відхиленням 1,0 для вас.
Просте додавання та / або множення змінить середнє та стандартне відхилення у ваші потреби.
Стандартний модуль бібліотеки Python випадковий має те, що ви хочете:
нормальний (мю, сигма)
Нормальний розподіл. 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)
вийде нормально розподілене число з MEAN = 50 VARIANCE = 9.
Q Як я можу перетворити рівномірний розподіл (оскільки виробляє більшість генераторів випадкових чисел, наприклад, від 0,0 до 1,0), у звичайний розподіл?
Для реалізації програмного забезпечення я знаю пару випадкових імен генераторів, які дають вам псевдо рівномірну випадкову послідовність у [0,1] (Mersenne Twister, Linear Congruate Generator). Назвемо це U (x)
Існує математична область, яка називається теорією доказовості. Перше: якщо ви хочете моделювати rv з інтегральним розподілом F, тоді ви можете спробувати просто оцінити F ^ -1 (U (x)). У пр.теорії було доведено, що такі rv матимуть інтегральний розподіл F.
Крок 2 може бути корисним для генерації rv ~ F без використання будь-яких методів підрахунку, коли F ^ -1 можна без проблем отримати аналітично. (наприклад, exp.distribution)
Для моделювання нормального розподілу можна зарахувати y1 * cos (y2), де y1 ~ рівномірно в [0,2pi]. і y2 - розподіл релея.
Питання: Що робити, якщо я хочу середнє і стандартне відхилення свого вибору?
Можна обчислити сигму * N (0,1) + m.
Можна показати, що таке зміщення і масштабування призводять до N (m, сигма)
Це реалізація Matlab з використанням полярної форми перетворення Box-Muller :
Функція 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 .
У мене є такий код, який, можливо, може допомогти:
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;
}