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 b
può 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 1
esattamente. Ciò accadrebbe solo se random.random()
è 1.0 exactly. However,
random () *never* produces
1.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 float
tipo 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 b
per alcuni valori di a
e b
. Ma 0
e 1
non 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 ** 53
valori 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 - a
consentire 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 a
e b
, come quando a
e b
hanno 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.max
e 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
X ~ U(0,1)
, alloraP(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