Random.uniform (0,1) può mai generare 0 o 1?


9

Nella documentazione si dice che esiste una possibilità che uniform(0,1)può generare i valori 0e 1.

Ho corso uniform(0, 1)10000 volte, ma non ha mai prodotto zero. Anche nel caso di uniform(0, 0.001).

Può random.uniform(0,1)mai generare 0o 1?


3
È teoricamente possibile, ma praticamente non accadrà mai. Matematicamente, una variabile casuale uniforme standard può assumere qualsiasi valore nell'intervallo da 0 a 1. se X ~ U(0,1), allora P(X=x)è quasi sicuramente 0, per tutti i valori di x. (Questo perché ci sono infiniti valori possibili nell'intervallo.) Se stai cercando esattamente 0 o 1, dovresti usare una funzione diversarandom.choice
pault

1
@pault ha quasi sicuramente un significato molto specifico in matematica, il che non ha molto senso qui poiché abbiamo una distribuzione discreta non un intervallo continuo. Ci sono solo un numero finito di galleggianti tra 0 e 1.
wim

2
@pault Allora perché parli matematicamente quando l'OP chiede l'implementazione di random.uniform?
mercoledì

1
Se quella documentazione è accurata, sono un po 'curioso di sapere come è stato prodotto per produrre sia 0 che 1. Sembra che [0, 1) sarebbe molto più semplice (che è come Math.random()funziona in JavaScript, ad esempio).
Ry-

1
Bounty di 50 punti per la prima persona che pubblica un seme casuale che fa random.uniform(0, 1)tornare uno 0 alla prima chiamata
wim

Risposte:


13

uniform(0, 1)può produrre 0, ma non produrrà mai1 .

La documentazione indica che l'endpoint b potrebbe essere incluso nei valori prodotti:

Il valore del punto finale bpuò essere incluso o meno nell'intervallo in base all'arrotondamento in virgola mobile nell'equazione a + (b-a) * random().

Quindi uniform(0, 1), la formula 0 + (1-0) * random(), semplificata 1 * random(), dovrebbe essere in grado di produrre 1esattamente. Ciò accadrebbe solo se random.random()è 1.0 exactly. However,random () *never* produces1.0`.

Citando la random.random()documentazione :

Restituisce il prossimo numero in virgola mobile casuale nell'intervallo [0.0, 1.0).

La notazione [..., ...)significa che il primo valore fa parte di tutti i possibili valori, ma il secondo no. random.random()produrrà al massimo valori molto vicini a 1.0. Il floattipo di Python è un valore in virgola mobile IEEE 754 base64 , che codifica un numero di frazioni binarie (1/2, 1/4, 1/5, ecc.) Che compongono il valore e il valore random.random()prodotto è semplicemente la somma di un selezione casuale di quelle 53 di queste frazioni da 2 ** -1(1/2) a 2 ** -53(1/9007199254740992).

Tuttavia, poiché può produrre valori molto vicini a 1.0, insieme agli errori di arrotondamento che si verificano quando si moltiplicano i nubmer a virgola mobile, è possibile produrre bper alcuni valori di ae b. Ma 0e 1non sono tra questi valori.

Nota che random.random() può produrre 0.0, quindi aè sempre incluso nei valori possibili per random.uniform()( a + (b - a) * 0 == a). Poiché ci sono 2 ** 53valori diversi che random.random()possono produrre (tutte le possibili combinazioni di quelle 53 frazioni binarie), c'è solo una possibilità 1 in 2 ** 53(quindi 1 su 9007199254740992) che ciò accada mai.

Quindi il valore più alto possibile che random.random()può produrre è 1 - (2 ** -53); scegli semplicemente un valore abbastanza piccolo da b - aconsentire l'avvio degli arrotondamenti quando moltiplicato per random.random()valori più alti. Più piccolo b - aè, maggiori sono le possibilità che ciò accada:

>>> import random, sys
>>> def find_b():
...     a, b = 0, sys.float_info.epsilon
...     while random.uniform(a, b) != b:
...         b /= 2
...     else:
...         return b
...
>>> print("uniform(0, {0}) == {0}".format(find_b()))
...
uniform(0, 4e-323) == 4e-323

Se colpisci b = 0.0, allora abbiamo diviso 1023 volte, il valore sopra significa che siamo stati fortunati dopo 1019 divisioni. Il valore più alto che ho trovato finora (eseguendo la funzione sopra in un ciclo con max()) è 8.095e-320(1008 divisioni), ma probabilmente ci sono valori più alti. È tutto un gioco d'azzardo. :-)

Può anche succedere se non ci sono molti passaggi discreti tra ae b, come quando ae bhanno un esponente elevato e quindi può sembrare molto distante. I valori in virgola mobile sono ancora solo approssimazioni e il numero di valori che possono codificare è finito. Ad esempio, esiste solo 1 frazione binaria di differenza tra sys.float_info.maxe sys.float_info.max - (2 ** 970), quindi esiste una probabilità 50-50 che random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max)produce sys.float_info.max:

>>> a, b = sys.float_info.max - (2 ** 970), sys.float_info.max
>>> values = [random.uniform(a, b) for _ in range(10000)]
>>> values.count(sys.float_info.max)  # should be roughly 5000
4997

5

"Più volte" non è abbastanza. 10.000 non sono abbastanza. random.uniformsceglie tra 2 ^ 53 (9.007.199.254.740.992) valori diversi. Sei interessato a due di loro. Pertanto, dovresti aspettarti di generare diversi quadrilioni di valori casuali prima di ottenere un valore esattamente pari a 0 o 1. Quindi è possibile, ma è molto probabile che non lo osserverai mai.


2
Perché uniform(0, 1)è impossibile produrre 1come risultato. Questo perché la funzione è semplicemente definita come def uniform(a, b): return a + (b - a) * random()e random()non può mai produrre 1.0.
Martijn Pieters

2
@MartijnPieters Credo che tu abbia ragione e ho votato a favore della tua risposta. Ho sospettato altrettanto, ma non ero sicuro, ed era a parte la spinta principale della mia risposta, quindi ho lasciato che fosse :)
hobbs

1

Puoi provare a generare un ciclo che conteggi la quantità di iterazioni necessarie per visualizzare uno 0 esatto (non farlo).

Inoltre, come affermato da Hobbs, la quantità di valori oggetto di uniformlycampionamento è 9.007.199.254.740.992. Ciò significa che la probabilità di vedere uno 0 è esattamente 1 / 9.007.199.254.740.992. Il che in termini generali e di arrotondamento significa che avrai bisogno in media di 10 quatrilioni di campioni per trovare uno 0. Naturalmente potresti trovarlo nei tuoi primi 10 tentativi, o mai più.

Il campionamento di un 1 è impossibile poiché l'intervallo definito per i valori viene chiuso con una parentesi, quindi non include 1.


1

Sicuro. uniform(0, 0.001)Invece eri già sulla buona strada con provare . Continua a limitare i limiti abbastanza da farlo accadere prima.

>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0
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.