Theoretisch optimaler Algorithmus
Hier ist eine Verbesserung der anderen Antwort, die ich gepostet habe. Die andere Antwort hat den Vorteil, dass es einfacher ist, auf den allgemeineren Fall des Erzeugens einer diskreten Verteilung aus einer anderen zu erweitern. Tatsächlich ist die andere Antwort ein Sonderfall des Algorithmus, der auf Han und Hoshi zurückzuführen ist.
Der Algorithmus, den ich hier beschreiben werde, basiert auf Knuth und Yao (1976). In ihrer Arbeit haben sie auch bewiesen, dass dieser Algorithmus die minimal mögliche erwartete Anzahl von Münzwürfen erreicht.
Betrachten Sie zur Veranschaulichung die in anderen Antworten beschriebene Ablehnungsstichprobenmethode. Angenommen, Sie möchten eine von 5 Zahlen einheitlich generieren [0, 4]. Die nächste Potenz von 2 ist 8, also wirfst du die Münze dreimal und erzeugst eine Zufallszahl bis 8. Wenn die Zahl 0 bis 4 ist, gibst du sie zurück. Andernfalls werfen Sie es aus und generieren eine weitere Zahl bis zu 8 und versuchen es erneut, bis Sie erfolgreich sind. Aber wenn Sie die Zahl wegwerfen, haben Sie nur etwas Entropie verschwendet. Sie können stattdessen die Anzahl der geworfenen Münzen bestimmen, um die Anzahl der künftigen Münzwürfe zu verringern, die Sie erwartungsgemäß benötigen. Konkret: Wenn Sie die Zahl [0, 7] generiert haben, geben Sie zurück, wenn es [0, 4] ist. Ansonsten ist es 5, 6 oder 7, und Sie tun in jedem Fall etwas anderes. Wenn es 5 ist, wirf die Münze erneut und gib basierend auf dem Wurf entweder 0 oder 1 zurück. Wenn es 6 ist, Wirf die Münze und gib entweder 2 oder 3 zurück. Wenn es 7 ist, wirf die Münze um. Wenn es Köpfe sind, gebe 4 zurück, wenn es Schwänze sind, fange von vorne an.
Die übrig gebliebene Entropie aus unserem ersten fehlgeschlagenen Versuch ergab 3 Fälle (5, 6 oder 7). Wenn wir dies einfach wegwerfen, werfen wir log2 (3) Münzwürfe weg. Wir behalten es stattdessen und kombinieren es mit dem Ergebnis eines anderen Flip, um 6 mögliche Fälle (5H, 5T, 6H, 6T, 7H, 7T) zu generieren, die wir sofort erneut versuchen, eine endgültige Antwort mit Erfolgswahrscheinlichkeit 5/6 zu generieren .
Hier ist der Code:
# 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)