Нужна помощь в генерации дискретных случайных чисел из распределения

Я искал на сайте, но не нашел именно то, что искал ... Я хотел сгенерировать дискретное случайное число из нормального распределения.

Например, если у меня есть диапазон от минимум 4 до максимум 10 и в среднем 7. Какой код или вызов функции (предпочтительнее для цели C) мне нужно вернуть число из этого диапазона. Естественно, из-за нормального распределения большее количество возвращенных чисел будет сосредоточено вокруг среднего числа 7.

В качестве второго примера, может ли колоколообразная кривая / распределение быть смещенным в сторону одного конца другого? Допустим, мне нужно сгенерировать случайное число с диапазоном от минимум 4 до максимум 10, и я хочу, чтобы большинство чисел возвращалось в центр вокруг числа 8 с естественным падением на основе наклонной колоколообразной кривой.

Любая помощь приветствуется ....

Энтони


person Community    schedule 28.07.2009    source источник
comment
Нормальное распределение не имеет конечных точек: оно имеет бесконечную поддержку.   -  person Sinan Ünür    schedule 29.07.2009
comment
Ему нужен целочисленный диапазон, в котором у одного броска двух кубиков есть верх и низ. Не имеет значения, нет ли у отдельного рулона решки. Будет раздача многих бросков.   -  person Nosredna    schedule 29.07.2009
comment
довольно сложно ответить на ваш вопрос, если вы не можете более точно определить, чего вы хотите. на основе наклонной колоколообразной кривой = ???   -  person Jason S    schedule 29.07.2009
comment
Возможно, более подходящим является биномиальное распределение?   -  person Dan Dyer    schedule 30.07.2009


Ответы (5)


Зачем тебе это нужно? Сможете ли вы сделать это как игрок в кости?

Сгенерируйте два случайных целых числа в диапазоне от 2 до 5 (включительно, конечно) и сложите их вместе. Или подбросьте монету (0,1) шесть раз и прибавьте к результату 4.

Суммирование нескольких кубиков дает нормальное распределение («колоколообразную кривую»), исключая высокие или низкие броски можно использовать для искажения распределения по-разному.

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


Конечно, чем больше бросков, тем точнее вы приближаетесь к кривой нормального распределения. Прокатка одного штампа дает ровную линию. Бросок двух кубиков просто создает подъем и спуск, который не очень похож на колокол. Шесть подбрасываний монеты приближают вас.

Так что подумайте об этом ...

Если я правильно понимаю ваш вопрос, у вас есть только семь возможных результатов - целые числа (4,5,6,7,8,9,10). Вы можете настроить массив из семи вероятностей, чтобы приблизиться к любому распределению, которое вам нравится.

person Nosredna    schedule 29.07.2009

Это встроено во многие фреймворки и библиотеки.

Кроме того, как и в TokenMacGuy нормальный распределение характеризуется не интервалом, на котором оно определено, а двумя параметрами: средним μ и стандартным отклонением σ. с обоими этими параметрами. вы можете ограничить определенный квантиль распределения на определенный интервал, так что 95% всех точек попадают в этот интервал. Но полностью ограничить его любым интервалом, кроме (−∞, ∞), невозможно.

Существует несколько методов генерации значений с нормальным распределением из однородных случайных значений (которые генерируют большинство генераторов случайных или псевдослучайных чисел:

  • Преобразование Бокса-Мюллера, вероятно, является самым простым, хотя и не совсем быстрым для вычисления. В зависимости от количества цифр, которое вам нужно, его должно быть достаточно, и его, безусловно, очень легко написать.

  • Другой вариант - полярный метод Марсаглии, который обычно быстрее 1.

  • Третий метод - это алгоритм зиккурата, который значительно быстрее вычисляется, но гораздо сложнее программировать. Однако для приложений, которые действительно используют много случайных чисел, это может быть лучшим выбором.

Однако в качестве общего совета: не пишите это самостоятельно, если у вас есть доступ к библиотеке, которая уже генерирует для вас нормально распределенные случайные числа.


Для искажения вашего распределения я бы просто использовал обычное нормальное распределение, выбирая μ и σ соответственно для одной стороны вашей кривой, а затем определяя, на какой стороне желаемого значения точка упала, растягивая ее соответствующим образом, чтобы соответствовать желаемому распределению.


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


1 При тестировании с намеренно плохими генераторами случайных чисел (да, хуже, чем RANDU) я заметил, что полярный метод приводит к бесконечному циклу, отклоняющему каждую выборку. Однако этого не произойдет со случайными числами, которые соответствуют их обычным статистическим ожиданиям.

person Joey    schedule 29.07.2009

Да, есть сложные математические решения, но для «простых, но практичных» я бы согласился с комментарием Носредны. Для простого решения Java:

Random random=new Random();
public int bell7()
{
  int n=4;
  for (int x=0;x<6;++x)
    n+=random.nextInt(2);
  return n;
}

Если вы не являетесь языком Java, Random.nextInt (n) возвращает случайное целое число от 0 до n-1. Я думаю, что все остальное должно быть похоже на то, что вы увидите на любом языке программирования.

Если бы диапазон был большим, то вместо nextInt (2) я бы использовал там большее число, чтобы было меньше итераций в цикле, в зависимости от частоты вызовов и требований к производительности.

person Jay    schedule 29.07.2009

Дэн Дайер и Джей совершенно правы. На самом деле вам нужно биномиальное распределение, а не нормальное. Форма биномиального распределения очень похожа на нормальное распределение, но оно дискретное и ограниченное, тогда как нормальное распределение является непрерывным и неограниченным.

Код Джея генерирует биномиальное распределение с 6 попытками и 50% вероятностью успеха в каждом испытании. Если вы хотите «исказить» свое распределение, просто измените строку, которая решает, прибавлять ли 1 к n, чтобы вероятность была отличной от 50%.

person Yakbutter    schedule 12.08.2009

Нормальное распределение не описывается конечными точками. Обычно это описывается средним значением (которое вы дали равным 7) и его стандартным отклонением. Важной особенностью этого является то, что из этого распределения можно получить значение, выходящее далеко за пределы ожидаемого диапазона, хотя это будет исчезающе редко, чем дальше вы будете отклоняться от среднего.

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

F(x) = 0.5 - 0.5*erf( (x-μ)/(σ * sqrt(2.0)))

где erf() - это функция ошибки, которую можно описать серией Тейлора:

erf (z) = 2.0 / sqrt (2.0) * Σ n = 0 ((-1) n z 2n + 1) / (п! (2n + 1))

Я оставлю это как упражнение, чтобы перевести это на C.

Если вы предпочитаете не участвовать в упражнении, вы можете рассмотреть возможность использования Научная библиотека Gnu, в которой, помимо многих других функций, есть методика генерации случайных чисел в одном из многих распространенных распределений, одним из которых является распределение Гаусса (подсказка).

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

person SingleNegationElimination    schedule 28.07.2009
comment
На самом деле вам не нужно писать реализацию erf (x) на C, поскольку она включена в C99 в math.h. Кроме того, использование ряда Тейлора для вычислений - кошмар, поскольку он сходится очень очень медленно. Использование метода непрерывной дроби является обычным решением (см. Числовые рецепты на C или различные математические библиотеки, такие как Cephes или fdlibm). - person Joey; 29.07.2009
comment
а-хе. Получил всю эту информацию из Google. в основном википедия. Также я упомянул, что реализацию C следует оставить в качестве упражнения! - person SingleNegationElimination; 29.07.2009
comment
Что ж, это было бы бессмысленным упражнением, так как полученный код не принесет много пользы. Есть причина, по которой никто не использует ряд Тейлора для вычисления erf (x) :-) (как по скорости, так и по точности) - person Joey; 29.07.2009