Преобразование равномерного распределения в нормальное распределение - PullRequest
95 голосов
/ 16 сентября 2008

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

Ответы [ 16 ]

48 голосов
/ 16 сентября 2008

Алгоритм Зиккурата довольно эффективен для этого, хотя преобразование Бокса-Мюллера проще реализовать с нуля (и не безумно медленно).

40 голосов
/ 16 июля 2010

Существует множество методов:

  • Не не используйте Box Muller. Особенно, если вы рисуете много гауссовских чисел. Box Muller дает результат, который фиксируется между -6 и 6 (при условии двойной точности. Ситуация ухудшается с плавающей точкой). И это действительно менее эффективно, чем другие доступные методы.
  • Ziggurat - это хорошо, но требуется поиск в таблице (и некоторая подстройка под платформу из-за проблем с размером кэша)
  • Соотношение униформ - мой любимый, только несколько сложений / умножений и журнал 1/50 времени (например, посмотрите там ).
  • Инвертирование CDF является эффективным (и упускается из виду, почему?), У вас есть быстрые реализации, доступные при поиске в Google. Это обязательно для квазислучайных чисел.
26 голосов
/ 17 сентября 2008

Изменение распределения любой функции на другую включает использование обратной функции, которую вы хотите.

Другими словами, если вы стремитесь к определенной вероятностной функции p (x), вы получаете распределение, интегрируя по нему Икс)). Теперь используйте функцию случайной вероятности (которая имеет равномерное распределение) и приведите значение результата через функцию Inv (d (x)). Вы должны получить случайные значения, приведенные с распределением в соответствии с выбранной вами функцией.

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

Надеюсь, это помогло, и спасибо за небольшое замечание об использовании дистрибутива, а не самой вероятности.

20 голосов
/ 13 октября 2008

Вот реализация 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 */
}
5 голосов
/ 18 сентября 2008

Используйте центральную предельную теорему запись в википедии запись в матемиуме в ваших интересах.

Создайте n из равномерно распределенных чисел, сложите их, вычтите n * 0.5, и вы получите примерно нормальное распределение со средним значением, равным 0, и дисперсией, равной (1/12) * (1/sqrt(N)) (см. Википедия по равномерным распределениям за последний)

n = 10 дает вам что-то наполовину приличное быстро. Если вы хотите что-то более чем наполовину достойное, выбирайте решение Tylers (как отмечено в записи в википедии о нормальных дистрибутивах )

1 голос
/ 03 апреля 2016

Кажется невероятным, что я мог бы что-то добавить к этому после восьми лет, но для случая Java я хотел бы указать читателям на метод Random.nextGaussian () , который генерирует гауссово распределение с для вас значит 0,0 и стандартное отклонение 1,0.

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

1 голос
/ 14 октября 2011

Где R1, R2 - случайные равномерные числа:

НОРМАЛЬНОЕ РАСПРЕДЕЛЕНИЕ, с SD 1: sqrt (-2 * log (R1)) * cos (2 * pi * R2)

Это точно ... не нужно делать все эти медленные циклы!

1 голос
/ 16 сентября 2008

Стандартный библиотечный модуль Python random имеет то, что вы хотите:

Нормальное значение (мю, сигма)
Нормальное распределение. мю - это среднее, а сигма - стандартное отклонение.

Что касается самого алгоритма, взгляните на функцию в random.py в библиотеке Python.

Ручной ввод здесь

1 голос
/ 16 сентября 2008

Я бы использовал Box-Muller. Об этом две вещи:

  1. В результате вы получаете два значения за одну итерацию
    Обычно вы кэшируете одно значение, а возвращаете другое. При следующем вызове образца вы возвращаете кэшированное значение.
  2. Box-Muller дает Z-счет
    Затем необходимо масштабировать Z-показатель по стандартному отклонению и добавить среднее значение, чтобы получить полное значение в нормальном распределении.
0 голосов
/ 23 января 2019

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

n <- length(z)
t0 <- Sys.time()
z <- rnorm(n)
t1 <- Sys.time()
t1-t0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...