Generare numeri casuali distribuiti uniformemente usando una moneta


26

Hai una moneta. Puoi capovolgerlo tutte le volte che vuoi.

Volete generare un numero casuale tale che dove .a r < b r , a , b Z +rar<br,a,bZ+

La distribuzione dei numeri dovrebbe essere uniforme.

È facile se :ba=2n

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

Cosa succede se ?ba2n


Usa l'algoritmo Han-Hoshi: fondamentalmente dividi l'intervallo in due, usa il tuo bit casuale (lancio della moneta) per scegliere casualmente uno dei due intervalli, quindi ripeti questo processo sul lato che hai scelto fino a quando non finisci i bit. Questo ti darà un intervallo uniformemente distribuito da una partizione della linea reale. Più lanci hai, più preciso è l'intervallo.
zenna,

Risposte:


13

Quello che stai cercando è basato sul campionamento di rifiuto o sul metodo accetta-rifiuta (nota che la pagina Wiki è un po 'tecnica).

Questo metodo è utile in questi tipi di situazioni: vuoi scegliere un oggetto casuale da un set (un intero casuale nel set nel tuo caso), ma non sai come farlo, ma tu può scegliere un oggetto casuale da un set più grande contenente il primo set (nel tuo caso, per alcuni tale che ; questo corrisponde a lanci di monete).[ a , 2 k + a ] k 2 k + a b k[a,b][a,2k+a]k2k+abk

In uno scenario del genere, continui a scegliere gli elementi dal set più grande fino a quando non selezioni casualmente un elemento nel set più piccolo. Se il tuo set più piccolo è abbastanza grande rispetto al tuo set più grande (nel tuo caso, contiene al massimo il doppio di interi come , che è abbastanza buono), questo è efficiente.[ a , b ][a,2k+a][a,b]

Un esempio alternativo: supponi di voler scegliere un punto casuale all'interno di un cerchio con raggio 1. Ora, non è davvero facile trovare un metodo diretto per questo. Passiamo al metodo accetta-rifiuta: campioniamo i punti in un quadrato 1x1 che comprende il cerchio e testiamo se il numero che disegniamo si trova all'interno del cerchio.


3
Si noti che se rifiutiamo i campioni da per ottenere una distribuzione su , il numero previsto di iterazioni è (mentre eseguiamo un esperimento con distribuzione geometrica). B | A |AB|A||B|
Raffaello

Ricordo di aver visto da qualche parte che ciò non può essere fatto esattamente a meno che l'intervallo non sia una potenza di 2 (come è ovvio, ad esempio 1/3 non ha un'espansione binaria di terminazione).
vonbrand,

7

(tecnicismi: la risposta si adatta alla selezione del numero )ax<b

Dal momento che ti è permesso di lanciare la tua moneta tutte le volte che vuoi, puoi ottenere la tua probabilità di avvicinarti quanto più ti piace vicino scegliendo una frazione (usando la radice binaria: lanci la moneta per ogni cifra dopo il punto) e moltiplicare per per ottenere un numero compreso tra 0 e [ba-1] (arrotondando per difetto a un numero intero). Aggiungere questo numero per e il gioco è fatto.r b - a ar[0,1]rbaa

Esempio : dire . 1/3 in binario è 0,0101010101 .... Quindi, se il tuo inversione è compreso tra 0 e 0,010101 ... la tua scelta è . se è compreso tra 0,010101 .. e 0,10101010 ... la tua scelta sarà , altrimenti sarà .b a + 1 a + 2ba=3ba+1a+2

Se si ribalta la vostra moneta volte poi ogni numero compreso tra e saranno scelti con probabilità .a b 1tab1ba±2(t+1)


1
Questo non dà una distribuzione uniforme. Per alcune applicazioni (es. Cripto, a volte), questo può essere molto negativo.
Gilles 'SO- smetti di essere malvagio' il

3
@Gilles: può essere riparato per dare una distribuzione perfettamente uniforme lanciando fino a quando non è più possibile cambiare il risultato. Questa è la risposta più efficiente.
Neil G

@NeilG So che può essere risolto, ma risolverlo sarebbe una parte cruciale della risposta.
Gilles 'SO- smetti di essere malvagio' il

2
@Gilles: hai ragione. Potrebbe modificare la risposta per dire che puoi produrre una distribuzione perfettamente uniforme se capovolgi mentre . +1 da parte mia per il miglior tempo medio e il caso peggiore. (ba)(f+2t1)(ba)(f2t1)
Neil G

@NeilG, non può essere "riparato", in quanto esiste un numero considerevole di numeri interi che non hanno una frazione binaria di terminazione.
vonbrand

7

Scegli un numero nella prossima potenza maggiore di 2 range e scarta le risposte maggiori di .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
E perché funziona?
Raffaello

@Raphael sei scettico o vuoi semplicemente che il poster spieghi più in dettaglio?
Suresh,

1
@Suresh: quest'ultimo. Lo pseudo codice potrebbe essere lucidato un po ', ma implementa ciò che spiegano gli altri risponditori. Senza giustificazione, questa risposta non vale molto da sola.
Raffaello

6

Nessuno ha menzionato questo, per cui vorrei formalmente dimostrare che se è un dominio la cui dimensione non è una potenza di due, poi un numero finito di lanci di monete fiera non sono sufficienti a generare un membro uniformemente casuale di . Supponiamo di utilizzare monete per generare un membro della . Per ogni , la probabilità che il vostro algoritmo generato è del tipo per qualche intero . Il teorema fondamentale dell'aritmetica mostra che .D k D d D d A / 2 k A A / 2 k1 / | D |DDkDdDdA/2kAA/2k1/|D|

Se si desidera generare campioni uniformi indipendenti di , il numero previsto di lanci di monete necessari (sotto l'algoritmo ottimale) è approssimativamente. Più in generale, se si desidera generare da una distribuzione di entropia , in previsione sono necessari circa bit casuali. Un algoritmo che lo raggiunge è la decodifica aritmetica, applicata a una sequenza (apparentemente) infinita di bit casuali.D n log 2 | D | H n HnDnlog2|D|HnH


3

Se ba non è una potenza di 2, potresti dover lanciare molte monete per ottenere un risultato. Potresti anche non ottenere mai un risultato, ma è improbabile all'estremo.

metodi

Il metodo più semplice è generare un numero in [a, a + 2 ^ n), dove 2 ^ n> = ba, fino a quando non capita di atterrare in [a, b). Getti molta entropia con questo metodo.

Un metodo più costoso ti consente di mantenere tutta l'entropia, ma diventa molto costoso dal punto di vista computazionale all'aumentare del numero di lanci / lanci di dadi. Intuitivamente è come trattare le lancette della moneta come le cifre di un numero binario alla destra del punto decimale, convertendo quel numero dalla base 2 alla base ab dopo, e restituendo le cifre di quel numero quando diventano "bloccate".

Esempio

Il codice seguente converte i rotoli di un dado a faccia in giù equo in rotoli di un dado a faccia in giù (nel tuo caso n = 2, m = ab), con un costo marginale crescente all'aumentare del numero di rotoli. Notare la necessità di un tipo di numero Rational con precisione arbitraria. Una proprietà interessante è che la conversione da n-s a m-sided e di nuovo a n-sided restituirà il flusso originale, anche se forse ritardato di un paio di rotoli a causa del fatto che le cifre devono rimanere bloccate.

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;
        }
    }
}

"ma ciò è improbabile all'estremo" - la probabilità per questo evento è ; diciamo che "quasi sicuramente" non accade. 0
Raffaello

2

Genera un decimale binario. Invece di archiviarlo in modo esplicito, tieni traccia dei valori minimo e massimo possibili. Una volta che tali valori si trovano nello stesso numero intero, restituire quel numero intero. Lo schizzo del codice è sotto.

(Modifica) Spiegazione più completa: supponi di voler generare un numero intero casuale compreso tra 1 e 3 inclusi con 1/3 di probabilità ciascuno. Lo facciamo generando un decimale binario casuale reale, x nell'intervallo (0, 1). Se x <1/3, restituisce 1, altrimenti se x <2/3 restituisce 2, altrimenti restituisce 3. Invece di generare esplicitamente le cifre per x, teniamo semplicemente traccia dei valori minimi e massimi possibili di x. Inizialmente, il valore minimo di x è 0 e il massimo è 1. Se si capovolgono prima le teste, la prima cifra di x dietro il punto decimale (in binario) è 1. Il valore minimo possibile di x (in binario) diventa 0,100000 = 1/2 e il massimo è 0.111111111 = 1. Ora se la tua prossima svolta è la coda x inizia con 0.10. Il valore minimo possibile è 0,1000000 = 1/2 e il massimo è 0,1011111 = 3/4. Il valore minimo possibile di x è 1/2, quindi sai che ' s nessuna possibilità di restituire 1 poiché ciò richiede x <1/3. Puoi comunque restituire 2 se x finisce per essere 1/2 <x <2/3 o 3 se 2/3 <x <3/4. Supponiamo ora che il terzo capovolgere sia la coda. Quindi x deve iniziare con 0.100. Min = 0.10000000 = 1/2 e max = 0.100111111 = 5/8. Ora da 1/3 <1/2 <5/8 <2/3 sappiamo che x deve rientrare nell'intervallo (1/3, 2/3), quindi possiamo smettere di generare cifre di x e solo restituire 2.

Il codice fa essenzialmente questo tranne che invece di generare x tra 0 e 1 genera x tra aeb, ma il principio è lo stesso.

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

Nota: ho testato questo codice rispetto al metodo accetta / rifiuta ed entrambi producono distribuzioni uniformi. Questo codice richiede un minor numero di gettoni rispetto all'accettazione del rifiuto, tranne quando b - a è vicino alla potenza successiva di 2. Es. Se si desidera generare a = 0, b = 62, allora accetta / rifiuta funziona meglio. Sono stato in grado di dimostrare che questo codice può utilizzare in media non più di 2 gettoni in più rispetto all'accettazione / rifiuto. Dalla mia lettura, sembra che Knuth e Yao (1976) abbiano fornito un metodo per risolvere questo problema e abbiano dimostrato che il loro metodo è ottimale nel numero previsto di lanci di monete. Hanno inoltre dimostrato che il numero previsto di lanci deve essere maggiore dell'entropia di Shannon della distribuzione. Non sono riuscito a trovare una copia del testo del documento e sarei curioso di vedere qual è il loro metodo. (Aggiornamento: ho appena trovato un'esposizione di Knuth Yao 1976 qui:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf ma non l'ho ancora letto). Qualcuno ha anche menzionato Han Hoshi in questo thread che sembra essere più generale e lo risolve usando una moneta distorta. Vedi anche http://paper.ijcsns.org/07_book/200909/20090930.pdf di Pae (2009) per una buona discussione della letteratura.



1

Questa è una soluzione proposta per il caso in cui b - a non è uguale a 2 ^ k. Dovrebbe funzionare in un numero fisso di passaggi (non è necessario buttare via i candidati che sono al di fuori dell'intervallo previsto).

Tuttavia, non sono sicuro che sia corretto. Si prega di criticare e aiutare a descrivere l'esatta non uniformità in questo generatore di numeri casuali (se presente) e come misurarlo / quantificarlo.

Innanzitutto converti in un problema equivalente di generazione di numeri casuali distribuiti uniformemente nell'intervallo [0, z-1] dove z = b - a.

Inoltre, sia m = 2 ^ k la potenza più piccola di 2> = z.

Come per la soluzione sopra, abbiamo già un generatore di numeri casuali R (m) distribuito uniformemente nell'intervallo [0, m-1] (può essere fatto lanciando k monete, una per ogni 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

Il ciclo while viene eseguito al massimo 3 volte, fornendo il numero casuale successivo in un numero fisso di passaggi (caso migliore = caso peggiore).

Vedi un programma di test per i numeri [0,2] qui: http://pastebin.com/zuDD2V6H


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

Dai un'occhiata più da vicino allo pseudo codice e al codice collegato.
Emette

01/21/4

Puoi sostituire l'intera funzione con una sola riga: return s = (s + R (m))% z;
Yuval Filmus,

1

Algoritmo teoricamente ottimale

Ecco un miglioramento dell'altra risposta che ho pubblicato. L'altra risposta ha il vantaggio che è più facile estendere al caso più generale di generare una distribuzione discreta da un'altra. In effetti, l'altra risposta è un caso speciale dell'algoritmo dovuto a Han e Hoshi.

L'algoritmo che descriverò qui si basa su Knuth e Yao (1976). Nel loro documento, hanno anche dimostrato che questo algoritmo raggiunge il numero minimo possibile di lanci di monete.

Per illustrarlo, considera il metodo di campionamento del rifiuto descritto da altre risposte. Ad esempio, supponiamo di voler generare uno dei 5 numeri in modo uniforme [0, 4]. La potenza successiva di 2 è 8, quindi lanci la moneta 3 volte e generi un numero casuale fino a 8. Se il numero è compreso tra 0 e 4, la restituisci. Altrimenti, lo butti fuori e generi un altro numero fino a 8 e riprova fino a quando non ci riesci. Ma quando butti il ​​numero, hai appena sprecato dell'entropia. Puoi invece condizionare il numero che hai lanciato per ridurre il numero di lanci di monete futuri di cui avrai bisogno in attesa. Concretamente, una volta generato il numero [0, 7], se è [0, 4], restituire. Altrimenti, è 5, 6 o 7 e fai qualcosa di diverso in ogni caso. Se è 5, lancia nuovamente la moneta e restituisci 0 o 1 in base al lancio. Se è 6, lancia la moneta e restituisci 2 o 3. Se è 7, lancia la moneta; se è testa, restituisce 4, se è la coda ricominciare.

L'entropia rimanente del nostro tentativo iniziale fallito ci ha dato 3 casi (5, 6 o 7). Se lo buttiamo fuori, buttiamo via i lanci di monete log2 (3). Invece lo manteniamo e lo combiniamo con il risultato di un altro capovolgimento per generare 6 possibili casi (5H, 5T, 6H, 6T, 7H, 7T) che proviamo immediatamente a generare nuovamente una risposta finale con probabilità di successo 5/6 .

Ecco il codice:

# 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.