任意の離散分布に基づいて数値を生成する方法は?


28

任意の離散分布に基づいて数値を生成するにはどうすればよいですか?

たとえば、生成したい数字のセットがあります。次のように1〜3のラベルが付けられているとします。

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

基本的に、パーセンテージは、乱数ジェネレーターからの出力に現れる確率です。区間[0、1]で均一な分布を生成する疑似乱数ジェネレーターがあります。これを行う方法はありますか?

持つことができる要素の数に制限はありませんが、%は100%になります。


2
それがあなたの質問なら、タイトルに「...任意の離散分布」を指定することをお勧めします。連続した場合は異なります。
デビッドMカプラン

3
一般的な方法は、この例であろう累積確率、のリスト内にバイナリサーチを実行することである(0,0.04,0.54,1.0)。これは平均して、生成イベントごとにlog(n)/2プローブかかります。ない確率は非常に小さいされていない場合、あなたが得ることができるO(1)に等しく、離間値のベクトルを作成することによって、パフォーマンスを[0,1]の各値に結果を割り当て、(事前計算ステージにおいて)。たとえば、この例では、ベクトル(1,1,1,1,2,,2,3,,3)と(50 2の及び46 3'S)。ユニフォームを生成し、100倍して、このベクトルにインデックスを付けます:done。
whuber


「ここ」のリンクは、実際にこの質問にリンクしています。@ Glen_b ... copy-n-pasteエラーですか?
ブルザエモン

@buruzaemonありがとう、それは間違いでした。修正しました。
Glen_b -Reinstateモニカ

回答:


26

離散分布からのサンプリングに最適なアルゴリズムの1つは、エイリアスメソッドです。

エイリアスメソッドは、2次元データ構造を(効率的に)事前計算して、確率に比例する領域に長方形を分割します。

図

色によって区別されるよう- -参照サイトからこの概略図では、ユニットの高さの矩形領域は4種類に区分された割合で1 / 31 / 12、及び1 / 12、でこれらの確率で離散分布から繰り返しサンプリングするため。垂直ストリップの幅は一定(単位)です。それぞれが1つまたは2つの部分に分割されます。ピースのIDと垂直分割の位置は、列インデックスを介してアクセス可能なテーブルに保存されます。1/21/31/121/12

テーブルは2つの単純なステップ(各座標に1つ)でサンプリングでき、2つの独立した均一値と計算のみを生成する必要があります。これにより、他の応答で説明されているように、離散CDFを反転するために必要なO log n 計算が改善されます。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
個人的には、私はアルゴリズム(または必要な知識を学ぶためのどこか)を好むでしょう。なぜなら、私はこれを私が構築しているアプリに組み込むことを試みているからです:)あなたの答えをありがとう:)
FurtiveFelon

うーん、あなたがしたいことについてもう少し知っていると、私たちはあなたを導くのに役立ちます。それについて詳しく教えてください。(目的、コンテキストなど)
ドミニクコントワ

投票用です。例えば、私はたくさんの写真があり、一度に6つしかユーザーに見せることができません。一度にユーザーに「ベスト」を組み込みたいと思います、そしてユーザーは各写真に賛成票または反対票を投じることができます。現在動作する最も簡単な解決策は、私が概説したスキームです(各数字は写真を表し、すべての採決はその写真の確率を下げ、他のすべてを高める)
-FurtiveFelon

1
@ furtivefelon、Rからコードをいつでも移植できます。oコードからアルゴリズムを見つけて、再実装します。
mpiktas

おそらく、この特定の目的のためのいくつかの有名なソリューションが存在するので、Stack Overflowについて良い(より良い)アドバイスを得るかもしれないと考えています。最後のコメントの情報を質問に直接含めることもお勧めします。
ドミニクコントワ

19

あなたの例では、疑似ランダムUniform [0,1]値を描画し、それをUと呼びます。次に出力します:

U <0.04の場合は1

U> = 0.04およびU <0.54の場合は2

U> = 0.54の場合は3

指定された%がa、b、...の場合、単に出力

Uの場合、値1

U> = aおよびU <(a + b)の場合、値2

基本的に、%を[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 間隔をm[0,1]F(0,1)

I1I2Im

及びF 0 0。あなたの例では、m = 3およびIj=(F(j1),F(j))F(0)0m=3

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

以来、、およびF 2 = 0.54、およびF 3 = 1F(1)=.04F(2)=.54F(3)=1

次に、次のアルゴリズムを使用して、分布Fでを生成できます。XF

(1)を生成UUniform(0,1)

(2)もし次に、X = JUIjX=j

  • このステップでは、かどうかを調べることによって行うことができる以下の各よりも累積確率、およびどこ変化点(から見ると、使用しているプログラミングをどんな言語でブール演算子を使用しての問題でなければなりません)が発生すると、ベクトルの最初の場所を見つけます。UTRUEFALSEFALSE

注意正確に一つの間隔になりますIのJ彼らは互いに素とパーティションなので、[ 0 1 ]UIj[0,1]


Shouldn't those intervals all be half-closed? Otherwise the boundaries between intervals are not included.. ie. {[0,0.04), [0.04,0.54), [0.54,1]}
naught101

1
P(U=u)=0 for any point u (i.e. the Lebesgue measure of the half open interval is the same as that of the open interval) so I don't think it matters.
Macro

1
On a finite-precision digital machine, though, maybe someday before the end of the universe it will matter...
jbowman

1
Fair enough, @whuber, see my edit.
Macro

1
OK, that is an algorithm. BTW, why don't you just return something like min(which(u < cp))? It would be good to avoid recomputing the cumulative sum on each call, too. With that precomputed, the entire algorithm is reduced to min(which(runif(1) < cp)). Or better, because the OP asks to generate numbers (plural), vectorize it as n<-10; apply(matrix(runif(n),1), 2, function(u) min(which(u < cp))).
whuber

2

One simple algorithm is to start with your uniform random number and in a loop first subtract off the first probability, if the result is negative then you return the first value, if still positive then you go to the next iteration and subtract off the next probability, check if negative, etc.

This is nice in that the number of values/probabilities can be infinite but you only need to calculate the probabilities when you get close to those numbers (for something like generating from a Poisson or negative binomial distribution).

If you have a finite set of probabilities, but will be generating many numbers from them then it could be more efficient to sort the probabilities so that you subtract the largest first, then the 2nd largest next and so forth.


2

First of all, let me draw your attention to a python library with ready-to-use classes for either integer or floating point random number generation that follow arbitrary distribution.

Generally speaking there are several approaches to this problem. Some are linear in time, but require large memory storage, some run in O(n log(n)) time. Some are optimized for integer numbers and some are defined for circular histograms (for example: generating random time spots during a day). In the above mentioned library I used this paper for integer number cases and this recipe for floating point numbers. It (still) lacks circular histogram support and is generally messy, but it works well.


2

I had the same problem. Given a set where each item has a probability and whose items' probabilities sum up to one, I wanted to draw a sample efficiently, i.e. without sorting anything and without repeatedly iterating over the set.

The following function draws the lowest of N uniformly distributed random numbers within the interval [a,1). Let r be a random number from [0,1).

next(N,a)=1(1a)rN

You can use this function to draw an ascending series (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.


It appears the problem you are addressing abruptly changed in the second paragraph from one that samples from an arbitrary discrete distribution to sampling from a uniform distribution. Its solution appears not to be relevant to the question that was asked here.
whuber

I clarified the last part.
casi

Your answer still seems unrelated to the question. Could you perhaps provide a small but nontrivial worked example of your algorithm? Show us how it would generate a single draw from the set {1,2,3} according to the probabilities given in the question.
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

Thank you for the clarification (+1). It may be of interest to many readers that this isn't a simple random sample, because the outcomes appear in a predetermined, fixed order: a random permutation would have to be applied to the results in order to create a simple random sample. You might also be interested in a parallelizable version of this algorithm in which
aj=i=1jlog(ui)i=1N+1log(ui)
where u1,,uN+1 is a simple random sample of Uniform(0,1] variates.
whuber
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.