Как генерировать числа на основе произвольного дискретного распределения?

28

Как генерировать числа на основе произвольного дискретного распределения?

Например, у меня есть набор чисел, которые я хочу сгенерировать. Скажем, они помечены как 1-3 следующим образом.

1: 4%, 2: 50%, 3: 46%

По сути, проценты - это вероятность того, что они появятся в выходных данных генератора случайных чисел. У меня есть генератор псевдослучайных чисел, который будет генерировать равномерное распределение в интервале [0, 1]. Есть ли способ сделать это?

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

FurtiveFelon
источник
2
Я мог бы предложить указать «... произвольные дискретные распределения» в заголовке, если это ваш вопрос. Непрерывный случай отличается.
Дэвид М Каплан
3
Общий способ заключается в выполнении двоичного поиска в списке совокупных вероятностей, который в этом примере будет (0,0.04,0,54,1,0) . В среднем это занимает журнал(N)/2 зонда на событие генерации. Если ни одна вероятность не является чрезвычайно малой, вы можете получить производительность О(1) , создав вектор с равными интервалами значений в [0,1] и (на этапе предварительного вычисления), назначив результат каждому значению. Например, в этом примере вы можете создать вектор 50 2 и 46 3). Генерация униформы, умножение на 100 и индексирование в этот вектор: готово. (1,1,1,1,2,...,2,3,...,3)5046
whuber
Также смотрите здесь
Glen_b
Эта ссылка "здесь" на самом деле ссылается на этот самый вопрос, @Glen_b ... copy-n-paste error?
buruzaemon
@buruzaemon спасибо, да, это была ошибка; Я исправил это.
Glen_b

Ответы:

26

Одним из лучших алгоритмов выборки из дискретного распределения является метод псевдонимов .

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

фигура

В этой схеме из ссылочного сайта, прямоугольник единичной высоты был разделен на четыре вида областей - в дифференцирован по цвету - в пропорциях , 1 / 3 , 1 / 12 и 1 / 12 , в порядок выборки повторно из дискретного распределения с этими вероятностями. Вертикальные полосы имеют постоянную (единичную) ширину. Каждый разделен на одну или две части. Идентификационные данные частей и расположение вертикальных делений хранятся в таблицах, доступных через индекс столбца.1/21/31/121/12

Таблица может быть выбрана в два простых шага (по одному для каждой координаты), требующих генерации только двух независимых унифицированных значений и вычисления Это улучшает вычисление O ( log ( n ) ), необходимое для инвертирования дискретного CDF, как описано в других ответах здесь.O(1)O(log(n))

Лукас
источник
2
Этот алгоритм является наилучшим, только если вероятности являются дешевыми для вычисления. Например, если велико, лучше не строить целое дерево. N
вероятностная
3
+1 Пока это единственный ответ, чтобы предложить и описать эффективный алгоритм.
whuber
19

Вы можете сделать это легко в R, просто укажите нужный размер:

sample(x=c(1,2,3), size=1000, replace=TRUE, prob=c(.04,.50,.46))
Доминик Комтуа
источник
3
Лично я предпочел бы алгоритм (или где-нибудь, чтобы узнать необходимые знания), так как я пытаюсь включить это в приложение, которое я
создаю
Хммм хорошо ... Зная немного больше о том, что вы хотите сделать, помог бы нам направлять вас. Можете ли вы рассказать нам больше об этом? (Цель, контекст и т. Д.)
Доминик Комтуа
Это для голосования. Например, у меня есть куча фотографий, и я могу показать только 6 для пользователя за раз, я хотел бы включить «лучшее» для пользователя за раз, и пользователь может голосовать за или против на каждой фотографии , Самое простое решение, которое могло бы работать прямо сейчас, - это схема, которую я обрисовал (каждое число представляет фотографию, каждый отрицательный голос уменьшит вероятность на этой фотографии и увеличит все остальное)
FurtiveFelon
1
@furtivefelon, вы всегда можете перенести код из R, чтобы выяснить алгоритм из кода и переопределить его.
mpiktas
Я думаю, что вы могли бы получить несколько хороших (лучших) советов по переполнению стека, поскольку, вероятно, существуют некоторые хорошо известные решения для этой конкретной цели. Я предлагаю также включить информацию из вашего последнего комментария непосредственно в ваш вопрос.
Доминик Комтуа
19

В вашем примере, скажем, вы рисуете псевдослучайное значение Uniform [0,1] и называете его U. Затем выведите:

1, если U <0,04

2, если U> = 0,04 и U <0,54

3, если U> = 0,54

Если указанный% является a, b, ..., просто выведите

значение 1, если U

значение 2, если U> = a и U <(a + b)

и т.п.

По сути, мы отображаем% в подмножества [0,1], и мы знаем, что вероятность того, что равномерное случайное значение попадет в любой диапазон, является просто длиной этого диапазона. Упорядочение диапазонов кажется самым простым, если не уникальным, способом сделать это. Это предполагает, что вы спрашиваете только о дискретных распределениях; для непрерывного, может сделать что-то вроде «выборки отклонения» ( запись в Википедии ).

Дэвид М Каплан
источник
8
Алгоритм работает быстрее, если вы сортируете категории в порядке убывания вероятности. Таким образом, вы делаете меньше тестов (в среднем) на случайное число генерируемых.
jbowman
1
Просто добавьте быстрое примечание о сортировке - это будет эффективно только в том случае, если вы сделаете это один раз в начале схемы выборки - так что это не принесет пользы в случаях, когда вероятности сами отбираются как часть большей общей схемы ( например, а затем P r ( Y = j ) = p j ). Делая сортировку в этом случае, вы добавляете операцию сортировки к каждой итерации выборки, которая будет добавлять O ( n log ( n ) )pjDistPr(Y=j)=pjO(nlog(n))время каждой итерации. Тем не менее, в этом случае может быть полезно отсортировать по приблизительной оценке размера вероятностей в начале.
вероятностная
4

Предположим, есть возможных дискретных результатов. Вы делите интервал [ 0 , 1 ] на подынтервалы на основе функции кумулятивной массовой вероятности F , чтобы получить разделенный ( 0 , 1 ) интервалм[0,1]F(0,1)

я1я2ям

где и F ( 0 ) 0 . В вашем примере m = 3 ияJзнак равно(F(J-1),F(J))F(0)0мзнак равно3

I1=(0,.04),     I2=(.04,.54),     I3=(.54,1)

так как и F ( 2 ) = .54 и F ( 3 ) = 1 .F(1)=.04F(2)=.54F(3)=1

Затем вы можете сгенерировать с распределением F, используя следующий алгоритм:XF

(1) генерировать UUniform(0,1)

(2) Если , то X = j .UIjX=j

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

Отметим, что будет находиться точно в одном из интервалов I j, поскольку они не пересекаются и разбивают [ 0 , 1 ] .UIj[0,1]

макрос
источник
Разве эти интервалы не должны быть полузакрыты? В противном случае границы между интервалами не включены .. т.е. {[0,0.04), [0.04,0.54), [0.54,1]}
ничто 101
1
P(U=u)=0u
1
На цифровой машине конечной точности, хотя, может быть, когда-нибудь до конца вселенной это будет иметь значение ...
jbowman
1
Справедливо, @whuber, см. Мое редактирование.
Макрос
1
ОК, это алгоритм. Кстати, почему бы тебе просто не вернуть что-то подобное min(which(u < cp))? Также было бы хорошо избегать повторного вычисления совокупной суммы при каждом вызове. С этим предварительным вычислением весь алгоритм сокращается до min(which(runif(1) < cp)). Или лучше, потому что ОП просит генерировать числа ( множественное число ), векторизовать его как n<-10; apply(matrix(runif(n),1), 2, function(u) min(which(u < cp))).
9uber
2

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

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

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

Грег Сноу
источник
2

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

Вообще говоря, существует несколько подходов к этой проблеме. Некоторые из них линейны по времени, но требуют большого объема памяти, другие запускаются за время O (n log (n)). Некоторые оптимизированы для целых чисел, а некоторые определены для круговых гистограмм (например: генерация случайных временных точек в течение дня). В вышеупомянутой библиотеке я использовал эту статью для целых чисел и этот рецепт для чисел с плавающей запятой. У него (все еще) отсутствует поддержка круговой гистограммы, и он, как правило, грязный, но работает хорошо.

Борис Горелик
источник
2

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

Следующая функция рисует самый низкий из N равномерно распределенные случайные числа в интервале [a,1), Позволятьр быть случайным числом из [0,1),

следующий(N,a)знак равно1-(1-a)рN

Вы можете использовать эту функцию, чтобы нарисовать восходящий ряд(ai) of N uniformly distributed random numbers in [0,1). Here is an example with N=10:

a0=next(10,0)
a1=next(9,a0)
a2=next(8,a1)

a9=next(1,a8)

While drawing that ascending series (ai) of uniformly distributed numbers, iterate over the set of probabilities P which represents your arbitraty (yet finite) distribution. Let 0k<|P| be the iterator and pkP. After drawing ai, increment k zero or more times until p0pk>ai. Then add pk to your sample and move on with drawing ai+1.


Example with the op's set {(1,0.04),(2,0.5),(3,0.46)} and sample size N=10:

i  a_i    k  Sum   Draw
0  0.031  0  0.04  1
1  0.200  1  0.54  2
2  0.236  1  0.54  2
3  0.402  1  0.54  2
4  0.488  1  0.54  2
5  0.589  2  1.0   3
6  0.625  2  1.0   3
7  0.638  2  1.0   3
8  0.738  2  1.0   3
9  0.942  2  1.0   3

Sample: (1,2,2,2,2,3,3,3,3,3)


If you wonder about the next function: It is the inverse of the probability that one of N uniformly distributed random numbers lies within the interval [a,x) with x1.

casi
источник
Похоже, что проблема, которую вы решаете, внезапно изменилась во втором абзаце с выборки из произвольного дискретного распределения на выборку из равномерного распределения. Его решение, кажется, не имеет отношения к вопросу, который был задан здесь.
whuber
Я уточнил последнюю часть.
casi
Ваш ответ все еще не связан с вопросом. Не могли бы вы привести небольшой, но нетривиальный пример вашего алгоритма? Покажите нам, как это будет генерировать одну ничью из набора{1,2,3} в соответствии с вероятностями, приведенными в вопросе.
whuber
I added an example. My answer has something in common with David M Kaplan's answer (stats.stackexchange.com/a/26860/93386), but requires just one instead of N (= sample size) iterations over the set, at the expense of drawing N N-th roots. I profiled both procedures, and mine was much faster.
casi
Спасибо за разъяснения (+1). Многим читателям может быть интересно, что это не простая случайная выборка, потому что результаты появляются в заранее определенном, фиксированном порядке: случайная перестановка должна быть применена к результатам, чтобы создать простую случайную выборку. Вас также может заинтересовать распараллеливаемая версия этого алгоритма, в которой
aJзнак равноΣязнак равно1Jжурнал(Uя)Σязнак равно1N+1журнал(Uя)
где U1,...,UN+1 простая случайная выборка равномерных (0,1) переменных.
whuber