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)