Перетворення рівномірного розподілу в звичайний розподіл


106

Як я можу перетворити рівномірний розподіл (як виробляє більшість генераторів випадкових чисел, наприклад, від 0,0 до 1,0) у звичайний розподіл? Що робити, якщо я хочу середнього та стандартного відхилення свого вибору?


3
Чи є у вас специфікація мови чи це лише загальне запитання щодо алгоритму?
Білл Ящірка

3
Загальне питання алгоритму. Мені все одно, яка мова. Але я вважаю за краще, щоб відповідь не покладався на конкретні функціональні можливості, які забезпечує лише ця мова.
Терхорст

Відповіді:


47

Алгоритм Ziggurat досить ефективний для цього, хоча трансформацію Box-Muller легше здійснити з нуля (і не з розуму повільно).


7
Звичайні попередження про лінійні генератори конгруентних стосуються обох цих методів, тому використовуйте гідний нижній генератор. Ура.
dmckee --- кошеня колишнього модератора

3
Наприклад, Mersenee Twister, чи є у вас інші пропозиції?
Грегг Лінд

47

Існує маса методів:

  • Як НЕ використовувати Box Muller. Особливо, якщо ви намалюєте багато гауссових чисел. Box Muller дає результат, який затискається між -6 та 6 (припускаючи подвійну точність. Справа погіршується з поплавками.). І це дійсно менш ефективно, ніж інші доступні методи.
  • Ziggurat чудово, але потребує пошуку таблиці (а також певного налаштування для платформи через проблеми з розміром кешу)
  • Коефіцієнт уніформи є моїм улюбленим, лише кілька додавань / множень та журнал 1/50 часу (наприклад, дивіться там ).
  • Інвертування CDF є ефективним (і не помічено, чому?), У вас є швидкі реалізації його, якщо ви шукаєте в Google. Це обов’язково для квазі-випадкових чисел.

2
Ви впевнені в затисканні [-6,6]? Це досить вагомий момент, якщо це правда (і заслуговує на увагу на сторінці вікіпедії).
redcalx

1
@locster: ось що сказав мені мій вчитель (він вивчав такі генератори, і я довіряю його слову). Можливо, я зможу знайти вам довідку.
Олександр К.

7
@locster: ця небажана властивість поділяється також методом зворотного CDF. Дивіться cimat.mx/~src/prope08/randomgauss.pdf . Це можна полегшити, використовуючи рівномірний RNG, який має ненульову ймовірність отримати число з плаваючою точкою, дуже близьке до нуля. Більшість RNG не мають, оскільки вони генерують (як правило, 64 бітове) ціле число, яке потім відображається на [0,1]. Це робить ці методи непридатними для вибірки хвостів гауссових змінних (подумайте про ціни на низькі / високі страйкові варіанти в обчислювальному фінансуванні).
Олександр К.

6
@AlexandreC. Просто, щоб було зрозуміло в двох точках, використовуючи 64-бітні числа, хвости виходять або 8,57, або 9,41 (нижнє значення, що відповідає перетворенню на [0,1) перед тим, як приймати журнал). Навіть якщо затиснуті до [-6, 6] шанси опинитися поза цим діапазоном приблизно 1,98е-9, що досить добре для більшості людей навіть в науці. Для показників 8,57 та 9,41 це стає 1,04е-17 та 4,97е-21. Ці числа настільки малі, що різниця між вибіркою Box Muller і справжньою гауссовою вибіркою з точки зору зазначеної межі майже суто академічна. Якщо вам потрібно краще, просто складіть їх чотири і розділіть на 2.
CrazyCasta

6
Я думаю, що пропозиція не використовувати перетворення Box Muller вводить в оману для великого відсотка користувачів. Про обмеження чудово знати, але, як зазначає CrazyCasta, для більшості програм, які не сильно залежать від людей, що випадають, вам, мабуть, не потрібно про це турбуватися. Наприклад, якщо ви коли-небудь залежали від вибірки від звичайного за допомогою numpy, ви залежали від перетворення Box Muller (полярна форма координат) github.com/numpy/numpy/blob/… .
Андреас Гривас

30

Зміна розподілу будь-якої функції на іншу передбачає використання зворотної функції, яку ви хочете.

Іншими словами, якщо ви орієнтуєтесь на певну функцію ймовірності p (x), ви отримуєте розподіл, інтегруючи над ним -> d (x) = інтеграл (p (x)) і використовуючи його зворотну: Inv (d (x)) . Тепер скористайтеся функцією випадкової ймовірності (які мають рівномірний розподіл) і переведіть значення результату через функцію Inv (d (x)). Ви повинні отримати випадкові значення, подані з розподілом, відповідно до обраної вами функції.

Це загальний математичний підхід - використовуючи його, ви тепер можете вибрати будь-яку функцію ймовірності чи розподілу, якщо у вас є зворотне або хороше зворотне наближення.

Сподіваюсь, це допомогло і дякую за невелике зауваження щодо використання розподілу, а не самої ймовірності.


4
+1 Це непомітний метод генерування гауссових змінних, який працює дуже добре. Зворотний CDF в цьому випадку може бути ефективно обчислений методом Ньютона (похідна - e ^ {- t ^ 2}), початкове наближення легко отримати як раціональний дріб, тому вам потрібно 3-4 оцінки erf та exp. Це обов'язково, якщо ви використовуєте квазі випадкові числа - випадок, коли для отримання гауссова цифри потрібно використовувати саме одне єдине число.
Олександр К.

9
Зауважте, що вам потрібно інвертувати функцію кумулятивного розподілу, а не функцію розподілу ймовірностей. Олександр має на увазі це, але я подумав, що згадування про це
явніше

Ви можете використовувати PDF, якщо ви готові випадковим чином вибрати напрям щодо середнього; я розумію це правильно?
Марк Маккенна


1
Ось пов'язане питання в ДП з більш узагальненою відповіддю з приємним поясненням.
тире

23

Ось реалізація 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 */
}

5

Використовуйте центральну межу теореми вікіпедії для вступу mathworld в свою користь.

Створіть n з рівномірно розподілених чисел, підсумуйте їх, відніміть n * 0,5, і ви отримаєте приблизно нормальний розподіл із середнім рівним 0 та дисперсією, рівною (1/12) * (1/sqrt(N))(див. Вікіпедію про рівномірні розподіли для останнього)

n = 10 дає вам щось наполовину пристойне швидко. Якщо ви хочете чогось більш ніж наполовину гідного, займіться рішенням стилів (як зазначено у статті Вікіпедії про звичайні дистрибуції )


1
Це не дасть особливо близького нормального («хвости» або кінцеві точки не будуть близькими до реального нормального розподілу). Box-Muller краще, як запропонували інші.
Пітер К.

1
Box Muller також має неправильні хвости (він повертає число від -6 до 6 в подвійній точності)
Олександр C.

n = 12 (сума 12 випадкових чисел в діапазоні 0 до 1, і віднімання 6) призводить до stddev = 1 і середнього = 0. Потім це може бути використане для генерації будь-якого нормального розподілу. Просто помножте результат на потрібний stddev і додайте середнє значення.
JerryM

3

Я б використовував Box-Muller. Про це дві речі:

  1. Ви отримуєте два значення за ітерацію.
    Як правило, ви кешуєте одне значення, а інше повертаєте. Під час наступного виклику для вибірки ви повертаєте кешоване значення.
  2. Box-Muller дає Z-бал.
    Потім потрібно масштабувати Z-бал за стандартним відхиленням і додавати середнє значення, щоб отримати повне значення при нормальному розподілі.

Як ви масштабуєте Z-бал?
Терхорст

3
масштабований = середній + stdDev * zScore // дає нормальне значення (середнє значення, stdDev ^ 2)
yoyoyoyosef

2

Де R1, R2 - випадкові однакові числа:

НОРМАЛЬНЕ РОЗМІСТЕННЯ, з SD 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)

Це точно ... не потрібно робити усі ці повільні петлі!


Перш ніж хтось виправляв мене ... ось я надумав наближення: (1,5- (R1 + R2 + R3)) * 1,88. Мені це теж подобається.
Ерік Аронесті

2

Здається неймовірним, що я можу щось додати до цього через вісім років, але для випадку Java я хотів би вказати читачам на метод Random.nextGaussian () , який генерує розподіл Гаусса із середнім значенням 0,0 та стандартним відхиленням 1,0 для вас.

Просте додавання та / або множення змінить середнє та стандартне відхилення у ваші потреби.


1

Стандартний модуль бібліотеки Python випадковий має те, що ви хочете:

нормальний (мю, сигма)
Нормальний розподіл. mu - середнє значення, а сигма - це стандартне відхилення.

Щодо самого алгоритму, подивіться на функцію random.py в бібліотеці Python.

Запис вручну тут


2
На жаль, бібліотека python використовує Kinderman, AJ та Monahan, JF, "Комп'ютерне генерування випадкових змінних з використанням коефіцієнта рівномірних відхилень", ACM Trans Math Software, 3, (1977), pp257-260. При цьому використовуються дві рівномірні випадкові величини для генерування нормального значення, а не одиничного, тому не очевидно, як використовувати його як відображення, яке хотіла ОП.
Ян

1

Це моя реалізація 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))
}

0

Я те, що ви повинні спробувати це в EXCEL : =norminv(rand();0;1). Це призведе до отримання випадкових чисел, які повинні бути нормально розподілені з нульовою середньою величиною та об'єднати дисперсію. "0" можна поставити з будь-яким значенням, так що числа будуть бажаного середнього значення, а змінивши "1", ви отримаєте дисперсію, рівну квадрату вашого введення.

Наприклад: =norminv(rand();50;3)вийде нормально розподілене число з MEAN = 50 VARIANCE = 9.


0

Q Як я можу перетворити рівномірний розподіл (оскільки виробляє більшість генераторів випадкових чисел, наприклад, від 0,0 до 1,0), у звичайний розподіл?

  1. Для реалізації програмного забезпечення я знаю пару випадкових імен генераторів, які дають вам псевдо рівномірну випадкову послідовність у [0,1] (Mersenne Twister, Linear Congruate Generator). Назвемо це U (x)

  2. Існує математична область, яка називається теорією доказовості. Перше: якщо ви хочете моделювати rv з інтегральним розподілом F, тоді ви можете спробувати просто оцінити F ^ -1 (U (x)). У пр.теорії було доведено, що такі rv матимуть інтегральний розподіл F.

  3. Крок 2 може бути корисним для генерації rv ~ F без використання будь-яких методів підрахунку, коли F ^ -1 можна без проблем отримати аналітично. (наприклад, exp.distribution)

  4. Для моделювання нормального розподілу можна зарахувати y1 * cos (y2), де y1 ~ рівномірно в [0,2pi]. і y2 - розподіл релея.

Питання: Що робити, якщо я хочу середнє і стандартне відхилення свого вибору?

Можна обчислити сигму * N (0,1) + m.

Можна показати, що таке зміщення і масштабування призводять до N (m, сигма)


0

Це реалізація 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);цього - це результат: Box-Muller Matlab Histfit

Очевидно, це дійсно неефективно в порівнянні з вбудованим ретном Matlab .


0

У мене є такий код, який, можливо, може допомогти:

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)]

0

Також простіше використовувати реалізовану функцію rnorm (), оскільки це швидше, ніж записувати генератор випадкових чисел для нормального розподілу. Дивіться наступний код як доказ

n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0

-2
function distRandom(){
  do{
    x=random(DISTRIBUTION_DOMAIN);
  }while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
  return x;
}

Хоча повернення не гарантовано, чи не так? ;-)
Пітер К.

5
Випадкові числа занадто важливі, щоб залишити їх випадковістю.
Дрю Ноакс

Не відповідає на питання - нормальний розподіл має нескінченну область.
Метт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.