Tạo số ngẫu nhiên phân phối đồng đều bằng cách sử dụng đồng xu


25

Bạn có một đồng tiền. Bạn có thể lật nó nhiều lần như bạn muốn.

Bạn muốn tạo ra một số ngẫu nhiên một r < b nơi r , một , b Z + .rar<br,a,bZ+

Phân phối số lượng phải thống nhất.

Thật dễ dàng nếu :ba=2n

r = a + binary2dec(flip n times write 0 for heads and 1 for tails) 

Điều gì nếu ?ba2n


Sử dụng thuật toán Han-Hoshi - về cơ bản chia khoảng thời gian thành hai, sử dụng bit ngẫu nhiên của bạn (lật đồng xu) để chọn ngẫu nhiên một trong hai khoảng thời gian, sau đó lặp lại quy trình này ở bên bạn đã chọn cho đến khi bạn hết bit. Điều này sẽ cung cấp cho bạn một khoảng thời gian phân phối đồng đều từ một phân vùng của dòng thực. Bạn càng lật, khoảng thời gian càng chính xác.
zenna

Câu trả lời:


13

Những gì bạn đang tìm kiếm dựa trên lấy mẫu Từ chối hoặc phương pháp từ chối chấp nhận (lưu ý rằng trang Wiki là một chút kỹ thuật).

Phương pháp này hữu ích trong các loại tình huống này: bạn muốn chọn một số đối tượng ngẫu nhiên từ một tập hợp (một số nguyên ngẫu nhiên trong tập trong trường hợp của bạn), nhưng bạn không biết làm thế nào, nhưng bạn có thể chọn một số đối tượng ngẫu nhiên từ một tập hợp lớn hơn chứa các thiết lập đầu tiên (trong trường hợp của bạn, [ một , 2 k + một ] đối với một số k sao cho 2 k + một b , điều này tương ứng với k xu flips).[a,b][a,2k+a]k2k+abk

Trong kịch bản như vậy, bạn chỉ cần tiếp tục chọn các phần tử từ tập lớn hơn cho đến khi bạn chọn ngẫu nhiên một phần tử trong tập nhỏ hơn. Nếu bộ nhỏ hơn của bạn đủ lớn so với bộ lớn hơn của bạn (trong trường hợp của bạn, chứa nhiều nhất gấp đôi số nguyên so với [ a , b ] , đủ tốt), thì điều này là hiệu quả.[a,2k+a][a,b]

Một ví dụ khác: giả sử bạn muốn chọn một điểm ngẫu nhiên trong vòng tròn có bán kính 1. Bây giờ, thật không dễ để đưa ra phương pháp trực tiếp cho việc này. Chúng tôi chuyển sang phương pháp chấp nhận từ chối: chúng tôi lấy mẫu các điểm trong hình vuông 1x1 bao quanh vòng tròn và kiểm tra xem số chúng tôi vẽ có nằm trong vòng tròn không.


3
Lưu ý rằng nếu chúng tôi từ chối các mẫu từ để nhận phân phối trên B , số lần lặp dự kiến ​​là | Một |AB(khi chúng tôi thực hiện một thí nghiệm với phân phối hình học). |A||B|
Raphael

Tôi nhớ rằng đã thấy ở đâu đó rằng điều này không thể được thực hiện chính xác trừ khi phạm vi là lũy thừa 2 (theo lý do, ví dụ 1/3 không có kết thúc mở rộng nhị phân).
vonbrand

7

(tính kỹ thuật: câu trả lời phù hợp với lựa chọn số )ax<b

Vì bạn được phép lật đồng tiền của mình bao nhiêu lần tùy ý, bạn có thể nhận được xác suất của mình theo mức độ gần giống như bạn muốn bằng cách chọn một phân số (sử dụng cơ số nhị phân: bạn lật đồng xu cho mỗi chữ số sau điểm) và nhân r với b - a để lấy số từ 0 đến [ba-1] (làm tròn xuống một số nguyên). Thêm số này vào a và bạn đã hoàn thành.r[0,1]rbaa

Ví dụ : nói . 1/3 trong nhị phân là 0,0101010101 .... Sau đó, nếu lần lật của bạn nằm trong khoảng từ 0 đến 0,010101 ... lựa chọn của bạn là b . nếu nó là beween 0.010101 .. và 0.10101010 ... chọn của bạn sẽ được một + 1 , và nếu không nó sẽ là một + 2 .ba=3ba+1a+2

Nếu bạn lật đồng xu của mình lần thì mỗi số giữa ab sẽ được chọn với xác suất 1tab.1ba±2(t+1)


1
Điều này không cung cấp một phân phối thống nhất. Đối với một số ứng dụng (ví dụ như tiền điện tử, đôi khi), điều này có thể rất tệ.
Gilles 'SO- ngừng trở thành ác quỷ'

3
@Gilles: Có thể sửa lỗi để phân phối đồng đều hoàn hảo bằng cách lật cho đến khi kết quả không thể thay đổi. Đây là câu trả lời hiệu quả nhất.
Neil G

@NeilG Tôi biết nó có thể được sửa, nhưng sửa nó sẽ là một phần quan trọng của câu trả lời.
Gilles 'SO- ngừng trở thành ác quỷ'

2
(ba)(f+2t1)(ba)(f2t1)

@NeilG, nó không thể được "sửa", vì có một bộ số nguyên khá lớn không có phân số nhị phân kết thúc.
vonbrand

7

ba

n = b-a;
N = round_to_next_larger_power_of_2(n)
while (1) {
  x = random(0 included to N excluded);
  if (x < n) break;
}
r = a + x;

4
Và tại sao điều này làm việc?
Raphael

@Raphael bạn có hoài nghi không, hay bạn chỉ muốn người đăng giải thích chi tiết hơn?
Suresh

1
@Suresh: Cái sau. Mã giả có thể được đánh bóng một chút, nhưng nó thực hiện những gì người trả lời khác giải thích. Không có lời biện minh, câu trả lời này không có giá trị nhiều.
Raphael

6

DDkDdDdA/2kAA/2k1/|D|

nDnlog2|D|HnH


3

Nếu ba không phải là lũy thừa của 2 thì bạn có thể phải lật nhiều đồng xu để có kết quả. Bạn thậm chí có thể không bao giờ nhận được một kết quả, nhưng điều đó là không thể trong cực đoan.

Phương pháp

Phương pháp đơn giản nhất là tạo một số trong [a, a + 2 ^ n), trong đó 2 ^ n> = ba, cho đến khi một người xảy ra hạ cánh ở [a, b). Bạn vứt bỏ rất nhiều entropy với phương pháp này.

Một phương pháp đắt tiền hơn cho phép bạn giữ tất cả các entropy, nhưng trở nên rất đắt tiền khi tính toán khi số lần lật đồng xu / cuộn xúc xắc tăng lên. Theo trực giác, nó giống như xử lý đồng xu lật như các chữ số của số nhị phân ở bên phải dấu thập phân, chuyển đổi số đó từ cơ sở 2 sang cơ sở ab sau và trả về các chữ số của số đó khi chúng bị 'kẹt'.

Thí dụ

Đoạn mã sau đây chuyển đổi các cuộn của một khuôn mặt n công bằng thành các cuộn của một mặt phẳng công bằng (trong trường hợp của bạn n = 2, m = ab), với chi phí cận biên tăng khi số lượng cuộn tăng lên. Lưu ý sự cần thiết của một loại số Rational với độ chính xác tùy ý. Một đặc tính tốt là chuyển đổi từ n-side sang m-side và quay lại n-side sẽ trả về luồng ban đầu, mặc dù có thể bị trì hoãn bởi một vài cuộn do các chữ số phải bị kẹt.

public static IEnumerable<BigInteger> DigitConversion(this IEnumerable<BigInteger> inputStream, BigInteger modIn, BigInteger modOut) {
    //note: values are implicitly scaled so the first unfixed digit of the output ranges from 0 to 1
    Rational b = 0; //offset of the chosen range
    Rational d = 1; //size of the chosen range
    foreach (var r in inputStream) {
        //narrow the chosen range towards the real value represented by the input
        d /= modIn;
        b += d * r;
        //check for output digits that have become fixed
        while (true) {
            var i1 = (b * modOut).Floor();
            var i2 = ((b + d) * modOut).Floor(); //note: ideally b+d-epsilon, but another iteration makes that correction unnecessary
            if (i1 != i2) break; //digit became fixed?
            //fix the next output digit (rescale the range to make next digit range from 0 to 1)
            d *= modOut;
            b *= modOut;
            b -= i1;
            yield return i1;
        }
    }
}

0

2

Tạo một số thập phân nhị phân. Thay vì lưu trữ nó một cách rõ ràng, chỉ cần theo dõi các giá trị tối thiểu và tối đa có thể. Khi các giá trị đó nằm trong cùng một số nguyên, trả về số nguyên đó. Phác thảo mã dưới đây.

(Chỉnh sửa) Giải thích đầy đủ hơn: Giả sử bạn muốn tạo một số nguyên ngẫu nhiên từ 1 đến 3, bao gồm 1/3 xác suất mỗi. Chúng tôi làm điều này bằng cách tạo ra một số thập phân nhị phân ngẫu nhiên thực, x trong phạm vi (0, 1). Nếu x <1/3, trả về 1, khác nếu x <2/3 trả về 2, khác trả về 3. Thay vì tạo các chữ số cho x một cách rõ ràng, chúng tôi chỉ theo dõi các giá trị tối thiểu và tối đa có thể có của x. Ban đầu, giá trị tối thiểu của x là 0 và tối đa là 1. Nếu bạn lật đầu trước thì chữ số đầu tiên của x sau dấu thập phân (trong nhị phân) là 1. Giá trị tối thiểu có thể có của x (trong nhị phân) sau đó trở thành 0.100000 = 1/2 và tối đa là 0.111111111 = 1. Bây giờ nếu lần lật tiếp theo của bạn là đuôi x bắt đầu bằng 0.10. Giá trị tối thiểu có thể là 0.1000000 = 1/2 và tối đa là 0.1011111 = 3/4. Giá trị tối thiểu có thể có của x là 1/2 để bạn biết có ' s không có cơ hội quay lại 1 vì yêu cầu x <1/3. Bạn vẫn có thể trả về 2 nếu x kết thúc là 1/2 <x <2/3 hoặc 3 nếu 2/3 <x <3/4. Bây giờ giả sử lật thứ ba là đuôi. Sau đó x phải bắt đầu bằng 0.100. Tối thiểu = 0.10000000 = 1/2 và tối đa = 0.100111111 = 5/8. Bây giờ kể từ 1/3 <1/2 <5/8 <2/3, chúng ta biết rằng x phải nằm trong khoảng (1/3, 2/3), vì vậy chúng ta có thể ngừng tạo các chữ số của x và chỉ cần trả về 2.

Mã về cơ bản thực hiện điều này ngoại trừ thay vì tạo x trong khoảng từ 0 đến 1, nó tạo ra x giữa a và b, nhưng nguyên tắc là như nhau.

def gen(a, b):
  min_possible = a
  max_possible = b

  while True:
    floor_min_possible = floor(min_possible)
    floor_max_possible = floor(max_possible)
    if max_possible.is_integer():
      floor_max_possible -= 1

    if floor_max_possible == floor_min_possible:
      return floor_max_possible

    mid = (min_possible + max_possible)/2
    if coin_flip():
      min_possible = mid
    else:
      max_possible = mid

Lưu ý: Tôi đã thử nghiệm mã này theo phương thức chấp nhận / từ chối và cả hai đều mang lại phân phối thống nhất. Mã này yêu cầu ít lần lật đồng xu hơn chấp nhận từ chối trừ khi b - a gần với sức mạnh tiếp theo của 2. Ex nếu bạn muốn tạo a = 0, b = 62 thì chấp nhận / từ chối sẽ tốt hơn. Tôi đã có thể chứng minh rằng mã này có thể sử dụng trung bình không quá 2 lần lật đồng xu hơn là chấp nhận / từ chối. Từ cách đọc của tôi, có vẻ như Knuth và Yao (1976) đã đưa ra một phương pháp để giải quyết vấn đề này và chứng minh rằng phương pháp của họ là tối ưu trong số lần lật đồng xu dự kiến. Họ tiếp tục chứng minh rằng số lần lật dự kiến ​​phải lớn hơn entropy của Shannon trong phân phối. Tuy nhiên, tôi không thể tìm thấy một bản sao của văn bản của bài báo và sẽ tò mò muốn xem phương pháp của họ là gì. (Cập nhật: vừa tìm thấy một giải trình của Knuth Yao 1976 tại đây:http://www.nrbook.com/devroye/Devroye_files/ch CHƯƠNG_fundred_1.pdf nhưng tôi chưa đọc nó). Ai đó cũng đề cập đến Han Hoshi trong chủ đề này có vẻ chung chung hơn và giải quyết nó bằng cách sử dụng một đồng tiền thiên vị. Xem thêm http://apers.ijcsns.org/07_book/200909/20090930.pdf của Pae (2009) để biết một cuộc thảo luận tốt về tài liệu.


1

Câu trả lời đơn giản?

bar

rba=2n+1n


Điều này dường như không hoàn thành.
Dave Clarke

1

Đây là một giải pháp được đề xuất cho trường hợp khi b - a không bằng 2 ^ k. Nó được cho là làm việc theo số bước cố định (không cần phải vứt bỏ các ứng cử viên nằm ngoài phạm vi dự kiến ​​của bạn).

Tuy nhiên, tôi không chắc điều này đúng. Vui lòng phê bình và giúp mô tả chính xác sự không đồng nhất trong trình tạo số ngẫu nhiên này (nếu có) và cách đo / định lượng nó.

Đầu tiên chuyển đổi sang vấn đề tương đương là tạo các số ngẫu nhiên phân bố đồng đều trong phạm vi [0, z - 1] trong đó z = b - a.

Ngoài ra, đặt m = 2 ^ k là công suất nhỏ nhất của 2> = z.

Theo giải pháp ở trên, chúng tôi đã có bộ tạo số ngẫu nhiên R (m) được phân phối đồng đều trong phạm vi [0, m - 1] (có thể được thực hiện bằng cách ném k xu, mỗi lần một bit).

    Keep a random seed s and initialize with s = R(m).   

    function random [0, z-1] :
        x = R(m) + s 
        while x >= z:
            x -= z
        s = x
        return x

Vòng lặp while chạy tối đa 3 lần, đưa ra số ngẫu nhiên tiếp theo theo số bước cố định (trường hợp tốt nhất = trường hợp xấu nhất).

Xem chương trình kiểm tra các số [0,2] tại đây: http://pastebin.com/zuDD2V6H


z=3m=41/2,1/4,1/4

Xin hãy xem mã giả cũng như mã được liên kết chặt chẽ hơn. Nó phát ra 0, 1 và 2 gần như với tần số bằng nhau ...
vpathak

01/21/4

Bạn có thể thay thế toàn bộ hàm bằng một dòng duy nhất: return s = (s + R (m))% z;
Yuval Filmus

1

Thuật toán tối ưu về mặt lý thuyết

Đây là một cải tiến của câu trả lời khác tôi đã đăng. Câu trả lời khác có ưu điểm là dễ dàng mở rộng sang trường hợp tổng quát hơn về việc tạo một phân phối rời rạc từ một phân phối khác. Trong thực tế, câu trả lời khác là một trường hợp đặc biệt của thuật toán do Han và Hoshi.

Thuật toán tôi sẽ mô tả ở đây dựa trên Knuth và Yao (1976). Trong bài báo của mình, họ cũng đã chứng minh rằng thuật toán này đạt được số lần lật đồng xu dự kiến ​​tối thiểu có thể.

Để minh họa nó, hãy xem xét phương pháp lấy mẫu Từ chối được mô tả bởi các câu trả lời khác. Ví dụ: giả sử bạn muốn tạo một trong 5 số thống nhất [0, 4]. Sức mạnh tiếp theo của 2 là 8 vì vậy bạn lật đồng xu 3 lần và tạo một số ngẫu nhiên lên đến 8. Nếu số đó là 0 đến 4 thì bạn trả lại. Nếu không, bạn ném nó ra và tạo một số khác lên đến 8 và thử lại cho đến khi bạn thành công. Nhưng khi bạn ném ra con số, bạn chỉ lãng phí một số entropy. Thay vào đó, bạn có thể dựa vào số lượng bạn đã ném ra để giảm số lần lật đồng xu trong tương lai mà bạn cần. Cụ thể, một khi bạn tạo số [0, 7], nếu đó là [0, 4], hãy trả về. Mặt khác, đó là 5, 6 hoặc 7 và bạn làm một cái gì đó khác nhau trong mỗi trường hợp. Nếu là 5, lật lại đồng xu và trả về 0 hoặc 1 dựa trên lần lật. Nếu là 6, lật đồng xu và trả lại 2 hoặc 3. Nếu là 7, hãy lật đồng xu; nếu nó đứng đầu, trả về 4, nếu đuôi bắt đầu lại.

Entropy còn sót lại từ lần thử thất bại ban đầu của chúng tôi đã cho chúng tôi 3 trường hợp (5, 6 hoặc 7). Nếu chúng ta chỉ ném nó ra, chúng ta sẽ ném đi log2 (3) đồng xu. Thay vào đó, chúng tôi giữ nó và kết hợp nó với kết quả của một lần lật khác để tạo ra 6 trường hợp có thể xảy ra (5H, 5T, 6H, 6T, 7H, 7T), chúng ta hãy thử lại ngay lập tức để tạo ra câu trả lời cuối cùng với xác suất thành công 5/6 .

Đây là mã:

# returns an int from [0, b)
def __gen(b):
  rand_num = 0
  num_choices = 1

  while True:
    num_choices *= 2
    rand_num *= 2
    if coin.flip():
      rand_num += 1

    if num_choices >= b:
      if rand_num < b:
        return rand_num
      num_choices -= b
      rand_num -= b

# returns an int from [a, b)
def gen(a, b):
  return a + __gen(b - a)
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.