Làm thế nào để tạo số dựa trên phân phối rời rạc tùy ý?


28

Làm cách nào để tạo số dựa trên phân phối rời rạc tùy ý?

Ví dụ, tôi có một bộ số mà tôi muốn tạo. Nói rằng chúng được dán nhãn từ 1-3 như sau.

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

Về cơ bản, tỷ lệ phần trăm là xác suất mà chúng sẽ xuất hiện trong đầu ra từ trình tạo số ngẫu nhiên. Tôi có một trình tạo số pesudorandom sẽ tạo phân phối đồng đều trong khoảng [0, 1]. Có cách nào để làm điều này?

Không có giới hạn về số lượng phần tử tôi có thể có, nhưng% sẽ thêm tới 100%.


2
Tôi có thể đề nghị chỉ định "... phân phối rời rạc tùy ý" trong tiêu đề, nếu đó là câu hỏi của bạn. Các trường hợp liên tục là khác nhau.
David M Kaplan

3
Một cách chung là thực hiện tìm kiếm nhị phân trong danh sách các xác suất tích lũy, trong ví dụ này sẽ là (0,0.04,0.54,1.0) . Tính trung bình, việc này sẽ log(n)/2 đầu dò cho mỗi sự kiện thế hệ. Nếu không có xác suất nào là rất nhỏ, bạn có thể nhận được hiệu suất O(1) bằng cách tạo một vectơ các giá trị cách đều nhau trong [0,1] và (trong giai đoạn tiền mã hóa) gán kết quả cho từng giá trị. Ví dụ, trong ví dụ này bạn có thể tạo vectơ (1,1,1,1,2,,2,3,,3) (với50 2 và46 3). Tạo đồng phục, nhân với 100 và lập chỉ mục vào vectơ này: xong.
whuber


Liên kết "ở đây" thực sự liên kết đến chính câu hỏi này, @Glen_b ... lỗi sao chép-n-paste?
buruzaemon

@buruzaemon cảm ơn, đó là một sai lầm; Tôi đã sửa nó.
Glen_b -Reinstate Monica

Câu trả lời:


26

Một trong những thuật toán tốt nhất để lấy mẫu từ một phân phối rời rạc là phương pháp bí danh .

Phương pháp bí danh (hiệu quả) sắp xếp trước cấu trúc dữ liệu hai chiều để phân vùng hình chữ nhật thành các khu vực tỷ lệ thuận với xác suất.

Figure

Trong sơ đồ này từ các trang web tham chiếu, một hình chữ nhật có chiều cao đơn vị đã được phân chia thành bốn loại các vùng - như phân biệt bằng màu sắc - theo tỷ lệ , 1 / 3 , 1 / 12 , và 1 / 12 , trong để lấy mẫu nhiều lần từ một phân phối rời rạc với các xác suất này. Các dải dọc có chiều rộng (đơn vị) không đổi. Mỗi phần được chia thành một hoặc hai mảnh. Các danh tính của các mảnh và vị trí của các phân chia dọc được lưu trữ trong các bảng có thể truy cập thông qua chỉ mục cột.1/21/31/121/12

Bảng có thể được lấy mẫu theo hai bước đơn giản (một cho mỗi tọa độ) yêu cầu chỉ tạo hai giá trị thống nhất độc lập và tính toán . Điều này cải thiện tính toán O ( log ( n ) ) cần thiết để đảo ngược CDF rời rạc như được mô tả trong các câu trả lời khác ở đây.O(1)O(log(n))


2
Thuật toán này chỉ tốt nhất nếu xác suất rẻ để tính toán. Ví dụ, nếu là rất lớn, tốt hơn là không xây dựng toàn bộ cây. n
xác suất

3
+1 Cho đến nay, đây là câu trả lời duy nhất để đề xuất và mô tả một thuật toán hiệu quả.
whuber

19

Bạn có thể làm điều này dễ dàng trong R, chỉ cần xác định kích thước bạn cần:

sample(x=c(1,2,3), size=1000, replace=TRUE, prob=c(.04,.50,.46))

3
Cá nhân, tôi thích một thuật toán (hoặc một nơi nào đó để tìm hiểu kiến ​​thức cần thiết), vì tôi đang cố gắng kết hợp nó vào một ứng dụng mà tôi đang xây dựng :) Cảm ơn rất nhiều vì câu trả lời của bạn :)
FurtiveFelon

Hmmm ok ... Biết thêm một chút về những gì bạn muốn làm sẽ giúp chúng tôi hướng dẫn bạn. Bạn có thể cung cấp cho chúng tôi nhiều thông tin hơn về nó? (Mục đích, bối cảnh, v.v.)
Dominic Comtois

Đó là để bỏ phiếu. Ví dụ: tôi có một loạt ảnh và tôi chỉ có thể hiển thị 6 cho người dùng một lần, tôi muốn kết hợp "tốt nhất" cho người dùng tại một thời điểm và người dùng có thể bỏ phiếu lên hoặc xuống trên mỗi ảnh . Giải pháp đơn giản nhất có thể hoạt động ngay bây giờ là sơ đồ tôi đã vạch ra (mỗi số đại diện cho một bức ảnh, mỗi phiếu bầu giảm sẽ làm giảm xác suất của bức ảnh đó và tăng trên mọi thứ khác)
FurtiveFelon

1
@furtivefelon, bạn luôn có thể chuyển mã từ R, tìm ra thuật toán từ mã và thực hiện lại nó.
mpiktas

Tôi nghĩ bạn có thể nhận được một số lời khuyên (tốt hơn) về Stack Overflow, vì có lẽ tồn tại một số giải pháp nổi tiếng cho mục đích cụ thể này. Tôi cũng đề nghị bao gồm thông tin từ bình luận cuối cùng của bạn trực tiếp vào câu hỏi của bạn.
Đaminh Comtois

19

Trong ví dụ của bạn, giả sử bạn vẽ giá trị Đồng phục giả [0,1] và gọi nó là U. Sau đó xuất ra:

1 nếu U <0,04

2 nếu U> = 0,04 và U <0,54

3 nếu U> = 0,54

Nếu% được chỉ định là a, b, ..., chỉ cần xuất ra

giá trị 1 nếu U

giá trị 2 nếu U> = a và U <(a + b)

v.v.

Về cơ bản, chúng tôi đang ánh xạ% vào các tập hợp con của [0,1] và chúng tôi biết xác suất giá trị ngẫu nhiên đồng nhất rơi vào bất kỳ phạm vi nào chỉ đơn giản là độ dài của phạm vi đó. Đặt các phạm vi theo thứ tự có vẻ đơn giản nhất, nếu không phải là duy nhất, để làm điều đó. Điều này giả định rằng bạn chỉ hỏi về các bản phân phối rời rạc; để liên tục, có thể làm một cái gì đó như "lấy mẫu từ chối" ( mục nhập Wikipedia ).


8
Thuật toán sẽ nhanh hơn nếu bạn sắp xếp các danh mục theo thứ tự xác suất giảm dần. Bằng cách đó, bạn thực hiện ít bài kiểm tra hơn (trung bình) cho mỗi số ngẫu nhiên được tạo.
jbowman

1
Chỉ cần thêm một ghi chú nhanh về cách sắp xếp - điều này sẽ chỉ hiệu quả nếu bạn thực hiện một lần khi bắt đầu sơ đồ lấy mẫu - vì vậy nó sẽ không hoạt động tốt trong trường hợp các xác suất được lấy mẫu như một phần của sơ đồ tổng thể lớn hơn ( ví dụ: và sau đó P r ( Y = j ) = p j ). Bằng cách sắp xếp trong trường hợp này, bạn sẽ thêm thao tác sắp xếp vào mỗi lần lặp lấy mẫu - sẽ thêm O ( n log ( n ) )pjDistPr(Y=j)=pjO(nlog(n))thời gian cho mỗi lần lặp. Tuy nhiên, có thể hữu ích để sắp xếp theo một phỏng đoán gần đúng ở kích thước của xác suất khi bắt đầu trong trường hợp này.
xác suất

4

Giả sử có kết quả rời rạc có thể. Bạn chia khoảng [ 0 , 1 ] vào subintervals dựa trên hàm xác suất khối lượng tích lũy, F , để cung cấp cho các phân vùng ( 0 , 1 ) khoảngm[0,1]F(0,1)

I1I2Im

nơi F ( 0 ) 0 . Trong ví dụ của bạn m = 3Ij=(F(j1),F(j))F(0)0m=3

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

kể từ F ( 2 ) = .54F ( 3 ) = 1 .F(1)=.04F(2)=.54F(3)=1

Sau đó, bạn có thể tạo với phân phối F bằng thuật toán sau:XF

(1) tạo UUniform(0,1)

(2) Nếu , sau đó X = j .UIjX=j

  • Bước này có thể được thực hiện bằng cách xem liệu có nhỏ hơn từng xác suất tích lũy hay không và xem điểm thay đổi (từ đến ) xảy ra ở đâu, đó là vấn đề sử dụng toán tử boolean trong bất kỳ ngôn ngữ lập trình nào bạn đang sử dụng và tìm nơi đầu tiên xảy ra trong vector.UTRUEFALSEFALSE

Lưu ý rằng sẽ nằm trong chính xác một trong các khoảng I j vì chúng tách rời nhau và phân vùng [ 0 , 1 ] .UIj[0,1]


Không nên đóng tất cả các khoảng đó? Mặt khác, ranh giới giữa các khoảng không được bao gồm .. tức là. {[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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.