Gleichmäßig verteilte Zufallszahlen mit einer Münze generieren


25

Du hast eine Münze. Sie können es so oft umdrehen, wie Sie möchten.

Sie möchten eine Zufallszahl erzeugen , , so dass ein r < b , wo r , a , b Z + .reinr<br,ein,bZ+

Die Verteilung der Nummern sollte gleichmäßig sein.

Es ist einfach, wenn :b-ein=2n

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

Was ist, wenn ?b-ein2n


Verwenden Sie den Han-Hoshi-Algorithmus - teilen Sie das Intervall grundsätzlich in zwei Teile auf, wählen Sie mit Ihrem Zufallsbit (Münzwurf) zufällig eines der beiden Intervalle aus und wiederholen Sie diesen Vorgang an der von Ihnen ausgewählten Seite, bis Ihnen die Bits ausgehen. Dies gibt Ihnen ein Intervall, das gleichmäßig von einer Partition der realen Linie verteilt ist. Je mehr Flips Sie haben, desto genauer ist das Intervall.
Zenna

Antworten:


13

Was Sie suchen, basiert auf dem Ablehnungs-Sampling oder der Accept-Reject-Methode (beachten Sie, dass die Wiki-Seite ein bisschen technisch ist).

Diese Methode ist in folgenden Situationen nützlich: Sie möchten ein zufälliges Objekt aus einer Menge auswählen ( in Ihrem Fall eine zufällige Ganzzahl in der Menge ), aber Sie wissen nicht, wie Sie das tun sollen, sondern Sie kann ein zufälliges Objekt aus einer größeren Menge auswählen, die die erste Menge enthält (in Ihrem Fall [ a , 2 k + a ] für einige k, so dass 2 k + a b ; dies entspricht k Münzwürfen).[ein,b][ein,2k+ein]k2k+einbk

In einem solchen Szenario wählen Sie einfach so lange Elemente aus der größeren Gruppe aus, bis Sie zufällig ein Element in der kleineren Gruppe auswählen. Wenn Ihre kleinere Menge im Vergleich zu Ihrer größeren Menge groß genug ist (in Ihrem Fall enthält höchstens doppelt so viele Ganzzahlen wie [ a , b ] , was gut genug ist), ist dies effizient.[ein,2k+ein][ein,b]

Ein alternatives Beispiel: Angenommen, Sie möchten einen zufälligen Punkt innerhalb eines Kreises mit Radius 1 auswählen. Nun ist es nicht einfach, eine direkte Methode dafür zu finden. Wir wenden uns der Accept-Reject-Methode zu: Wir tasten Punkte in einem 1 × 1-Quadrat ab, das den Kreis umgibt, und testen, ob die von uns gezogene Zahl innerhalb des Kreises liegt.


3
Beachten Sie, dass die erwartete Anzahl von Iterationen | ist , wenn wir Stichproben von ablehnen , um eine Verteilung auf B zu erhalten A |EINB(während wir ein Experiment mit geometrischer Verteilung durchführen). |EIN||B|
Raphael

Ich erinnere mich, dass ich irgendwo gesehen habe, dass dies nicht genau gemacht werden kann, wenn der Bereich nicht eine Potenz von 2 ist (wie es naheliegt, hat zB 1/3 keine terminierende binäre Erweiterung).
Vonbrand

7

(Technik: Die Antwort passt zur Auswahl der Zahl )ax<b

Da Sie Ihre Münze so oft werfen dürfen, wie Sie möchten, können Sie Ihre Wahrscheinlichkeit so nah wie Sie möchten bestimmen, indem Sie einen Bruch wählen (mit dem binären Radix: Sie werfen die Münze für jede Ziffer nach dem Punkt) und multiplizieren Sie r mit b - a , um eine Zahl zwischen 0 und [ba-1] zu erhalten (auf eine ganze Zahl abrunden). Fügen Sie diese Nummer zu a hinzu und Sie sind fertig.r[0,1]rbaa

Beispiel : Sagen Sie . 1/3 in binär ist 0.0101010101 .... Wenn dein Flip zwischen 0 und 0.010101 liegt ... ist deine Auswahl b . wenn es beween 0.010101 .. und 0.10101010 ... Ihre Wahl wird eine + 1 , und sonst wird es sein , eine + 2 .ba=3bein+1ein+2

Wenn Sie Ihre Münze mal werfen, wird jede Zahl zwischen a und b mit Wahrscheinlichkeit 1 ausgewähltteinb.1b-ein±2-(t+1)


1
Dies ergibt keine gleichmäßige Verteilung. Für einige Anwendungen (z. B. Krypto, manchmal) kann dies sehr schlecht sein.
Gilles 'SO - hör auf, böse zu sein'

3
@Gilles: Es kann fixiert werden, um durch Umdrehen eine perfekt gleichmäßige Verteilung zu erzielen, bis sich das Ergebnis nicht mehr ändern kann. Dies ist die effizienteste Antwort.
Neil G

@NeilG Ich weiß, dass es repariert werden kann, aber das Reparieren wäre ein entscheidender Teil der Antwort.
Gilles 'SO- hör auf böse zu sein'

2
@ Gilles: Du hast recht. Er könnte die Antwort ändern, dass Sie eine perfekt gleichmäßige Verteilung erzeugen können, wenn Sie drehen, während ( b - a ) ( f + 2 - t - 1 ) ( b - a ) ( f - 2 - t - 1) ) . +1 von mir für den besten Durchschnittsfall und die schlechteste Zeit. (b-ein)(f+2-t-1)(b-ein)(f-2-t-1)
Neil G

@NeilG, es kann nicht "repariert" werden, da es eine ziemlich große Menge von Ganzzahlen gibt, die keinen abschließenden binären Bruch haben.
Vonbrand

7

Wählen Sie eine Zahl in der nächstgrößeren Potenz von 2 und verwerfen Sie Antworten, die größer als .b-ein

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
Und warum funktioniert das?
Raphael

@Raphael sind Sie skeptisch oder möchten Sie das Plakat nur näher erläutern?
Suresh

1
@ Suresh: Letzteres. Der Pseudocode könnte ein bisschen aufpoliert werden, aber er implementiert, was andere Antwortende erklären. Ohne Begründung ist diese Antwort für sich genommen nicht viel wert.
Raphael

6

Niemand hat dies erwähnt, also lassen Sie mich formal beweisen, dass, wenn eine Domäne ist, deren Größe keine Zweierpotenz ist, endlich viele Münzwürfe nicht ausreichen, um ein einheitlich zufälliges Mitglied von D zu erzeugen . Angenommen, Sie verwenden k Münzen, um ein Mitglied von D zu generieren . Für jedes d D hat die Wahrscheinlichkeit, dass Ihr Algorithmus d generiert, für eine ganze Zahl A die Form A / 2 k . Der Hauptsatz der Arithmetik zeigt, dass A / 2 k1 / | D | .DDkDdDdEIN/2kEINEIN/2k1/|D|

Wenn Sie unabhängige, einheitliche Stichproben von D erzeugen möchten, benötigen Sie (unter dem optimalen Algorithmus) voraussichtlich n log 2 | D | . Wenn Sie von einer Verteilung der Entropie erzeugen wollen Allgemeiner H , müssen Sie ungefähr n H Zufallsbits in Erwartung. Ein Algorithmus, der dies erreicht, ist die arithmetische Decodierung, die auf eine (scheinbar) unendliche Folge von Zufallsbits angewendet wird.nDnLog2|D|HnH


3

Wenn ba keine Potenz von 2 ist, müssen Sie möglicherweise viele Münzen umwerfen, um ein Ergebnis zu erhalten. Sie können sogar nie ein Ergebnis erhalten, aber das ist im Extremfall unwahrscheinlich.

Methoden

Die einfachste Methode ist, eine Zahl in [a, a + 2 ^ n) zu erzeugen, wobei 2 ^ n> = ba ist, bis man zufällig in [a, b) landet. Mit dieser Methode werfen Sie viel Entropie weg.

Eine teurere Methode ermöglicht es Ihnen, die gesamte Entropie beizubehalten, wird jedoch rechnerisch sehr teuer, wenn die Anzahl der Münzwürfe / Würfelwürfe zunimmt. Intuitiv ist es so, als würde man die Münzwürfe als die Ziffern einer Binärzahl rechts vom Dezimalpunkt behandeln, diese Zahl von Basis 2 nach Basis ab konvertieren und die Ziffern dieser Zahl zurückgeben, wenn sie "hängen bleiben".

Beispiel

Der folgende Code konvertiert Rollen eines fairen n-seitigen Würfels in Rollen eines fairen m-seitigen Würfels (in Ihrem Fall n = 2, m = ab), wobei die Grenzkosten mit zunehmender Anzahl der Rollen steigen. Beachten Sie die Notwendigkeit eines Rationalen Zahlentyps mit willkürlicher Genauigkeit. Eine nette Eigenschaft ist, dass das Konvertieren von n-seitig zu m-seitig und zurück zu n-seitig den ursprünglichen Stream zurückgibt, obwohl dies möglicherweise durch ein paar Rollen verzögert wird, weil Ziffern stecken bleiben müssen.

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

Generieren Sie eine binäre Dezimalstelle. Anstatt sie explizit zu speichern, müssen Sie nur die möglichen Mindest- und Höchstwerte protokollieren. Wenn diese Werte in derselben Ganzzahl liegen, geben Sie diese Ganzzahl zurück. Die Skizze des Codes ist unten.

(Bearbeiten) Ausführlichere Erklärung: Angenommen, Sie möchten eine zufällige Ganzzahl von 1 bis einschließlich 3 mit einer Wahrscheinlichkeit von jeweils 1/3 generieren. Wir erzeugen dazu eine zufällige binäre Dezimalzahl real, x im Bereich (0, 1). Wenn x <1/3, gebe 1 zurück, andernfalls wenn x <2/3, gebe 2, andernfalls gebe 3 zurück. Anstatt die Ziffern für x explizit zu generieren, behalten wir nur die minimal und maximal möglichen Werte von x im Auge. Anfänglich ist der minimale Wert von x 0 und der maximale Wert 1. Wenn Sie die Köpfe zuerst umdrehen, ist die erste Stelle von x hinter dem Dezimalpunkt (in Binärform) 1. Der minimal mögliche Wert von x (in Binärform) wird dann 0,100000 = 1/2 und das Maximum ist 0,111111111 = 1. Wenn Ihr nächster Flip Tails ist, beginnt x mit 0,10. Der minimal mögliche Wert ist 0.1000000 = 1/2 und der maximal mögliche Wert ist 0.1011111 = 3/4. Der minimal mögliche Wert von x ist 1/2, damit Sie wissen, dass es Es gibt keine Chance, 1 zurückzugeben, da dies x <1/3 erfordert. Sie können immer noch 2 zurückgeben, wenn x 1/2 <x <2/3 ist, oder 3, wenn 2/3 <x <3/4 ist. Nehmen wir nun an, der dritte Flip ist Schwänze. Dann muss x mit 0.100 beginnen. Min = 0,10000000 = 1/2 und Max = 0,100111111 = 5/8. Jetzt, da 1/3 <1/2 <5/8 <2/3, wissen wir, dass x in das Intervall (1/3, 2/3) fallen muss, damit wir die Erzeugung von Ziffern von x stoppen und einfach 2 zurückgeben können.

Der Code tut dies im Wesentlichen mit der Ausnahme, dass er x nicht zwischen 0 und 1 generiert, sondern zwischen a und b, aber das Prinzip ist dasselbe.

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

Bemerkung: Ich habe diesen Code gegen die Accept / Reject-Methode getestet und beide ergeben gleichmäßige Verteilungen. Dieser Code erfordert weniger Münzwürfe als Accept / Reject, außer wenn b - a nahe an der nächsten Potenz von 2 liegt. Wenn Sie beispielsweise a = 0, b = 62 generieren möchten, ist Accept / Reject besser. Ich konnte nachweisen, dass dieser Code im Durchschnitt nicht mehr als 2 Münzwürfe verwenden kann, als angenommen / abgelehnt zu werden. Aus meiner Lektüre geht hervor, dass Knuth und Yao (1976) eine Methode zur Lösung dieses Problems angegeben und bewiesen haben, dass ihre Methode für die erwartete Anzahl von Münzwürfen optimal ist. Sie bewiesen ferner, dass die erwartete Anzahl von Flips größer sein muss als die Shannon-Entropie der Verteilung. Ich konnte jedoch keine Kopie des Textes des Papiers finden und wäre gespannt, was ihre Methode ist. (Update: Habe gerade eine Ausstellung von Knuth Yao 1976 hier gefunden:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf aber ich habe es noch nicht gelesen). Jemand erwähnte auch Han Hoshi in diesem Thread, der allgemeiner zu sein scheint und ihn mit einer voreingenommenen Münze löst. Siehe auch http://paper.ijcsns.org/07_book/200909/20090930.pdf von Pae (2009) für eine gute Diskussion der Literatur.


1

Die einfache Antwort?

b-einr

rb-ein=2n+1n


Dies scheint nicht vollständig zu sein.
Dave Clarke

1

Dies ist eine vorgeschlagene Lösung für den Fall, dass b - a nicht 2 ^ k ist. Es soll in einer festgelegten Anzahl von Schritten funktionieren (keine Notwendigkeit, Kandidaten, die außerhalb Ihres erwarteten Bereichs liegen, wegzuwerfen).

Ich bin mir jedoch nicht sicher, ob dies korrekt ist. Bitte kritisieren Sie und helfen Sie dabei, die genaue Ungleichmäßigkeit in diesem Zufallszahlengenerator (falls vorhanden) zu beschreiben und wie man sie misst / quantifiziert.

Konvertieren Sie zunächst in das äquivalente Problem der Erzeugung gleichmäßig verteilter Zufallszahlen im Bereich [0, z-1], wobei z = b - a.

Auch sei m = 2 ^ k die kleinste Potenz von 2> = z.

Gemäß der obigen Lösung haben wir bereits einen gleichmäßig verteilten Zufallszahlengenerator R (m) im Bereich [0, m - 1] (kann durch Werfen von k Münzen, eine für jedes Bit, durchgeführt werden).

    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

Die while-Schleife wird höchstens dreimal ausgeführt und gibt die nächste Zufallszahl in festen Schritten an (bester Fall = schlechtester Fall).

Ein Testprogramm für Nummern [0,2] finden Sie hier: http://pastebin.com/zuDD2V6H


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

Bitte schauen Sie sich den Pseudocode sowie den verlinkten Code genauer an. Es gibt 0, 1 und 2 fast mit der gleichen Häufigkeit aus ...
vpathak

01/21/4

Sie können die gesamte Funktion durch eine einzelne Zeile ersetzen: return s = (s + R (m))% z;
Yuval Filmus

1

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.