uniform(0, 1)
może produkować 0
, ale nigdy nie będzie produkować 1
.
Dokumentacja mówi, że punkt końcowy b
mógł być włączone do wartości wytworzonych:
Wartość punktu końcowego b
może, ale nie musi być zawarta w zakresie, w zależności od zaokrąglenia zmiennoprzecinkowego w równaniu a + (b-a) * random()
.
Tak więc uniform(0, 1)
, w 0 + (1-0) * random()
uproszczeniu, formuła 1 * random()
musiałaby być w stanie wytworzyć 1
dokładnie. To by się stało tylko, jeśli random.random()
jest 1.0 exactly. However,
losowo () *never* produces
1.0`.
Cytując random.random()
dokumentację :
Zwraca kolejną losową liczbę zmiennoprzecinkową z zakresu [0,0, 1,0).
Notacja [..., ...)
oznacza, że pierwsza wartość jest częścią wszystkich możliwych wartości, ale druga nie. random.random()
co najwyżej wytworzy wartości bardzo zbliżone do 1.0
. float
Typ Pythona to zmiennoprzecinkowa wartość IEEE 754 base64 , która koduje pewną liczbę ułamków binarnych (1/2, 1/4, 1/5 itd.) , Które składają się na wartość, a random.random()
wytworzona wartość jest po prostu sumą losowy wybór tych 53 takich frakcji od 2 ** -1
(1/2) do 2 ** -53
(1/9007199254740992).
Jednakże, ponieważ może wytwarzać wartości bardzo blisko 1.0
, razem z zaokrąglenia błędy występujące podczas pomnożyć pływających nubmers momencie, może produkować b
dla pewnych wartości a
i b
. Ale 0
i 1
nie należą do tych wartości.
Zauważ, że random.random()
może produkować 0,0, więc a
jest zawsze uwzględniany w możliwych wartościach dla random.uniform()
( a + (b - a) * 0 == a
). Ponieważ istnieją 2 ** 53
różne wartości, które random.random()
mogą wytworzyć (wszystkie możliwe kombinacje tych 53 ułamków binarnych), istnieje tylko 1 na 1 2 ** 53
(a więc 1 na 9007199254740992) szansa na takie zdarzenie.
Zatem najwyższą możliwą wartością, jaką random.random()
można wytworzyć, jest 1 - (2 ** -53)
; po prostu wybierz wystarczająco małą wartość, b - a
aby umożliwić zaokrąglanie, gdy zostanie pomnożone przez wyższe random.random()
wartości. Im mniejsze b - a
, tym większe są szanse, że się to wydarzy:
>>> 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
Jeśli trafisz b = 0.0
, podzieliliśmy 1023 razy, powyższa wartość oznacza, że mieliśmy szczęście po 1019 dywizjach. Najwyższą wartością, jaką do tej pory znalazłem (uruchamianie powyższej funkcji w pętli z max()
) jest 8.095e-320
(1008 działów), ale prawdopodobnie są wyższe wartości. To gra losowa. :-)
Może się to również zdarzyć, jeśli nie ma wielu dyskretnych kroków pomiędzy a
i b
, na przykład kiedy a
i b
mają wysoki wykładnik, a więc może wydawać się dalekie. Wartości zmiennoprzecinkowe są nadal jedynie przybliżeniami, a liczba wartości, które mogą kodować, jest skończona. Na przykład, istnieje tylko 1 binarna część różnicy między sys.float_info.max
i sys.float_info.max - (2 ** 970)
, więc istnieje szansa 50-50 random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max)
produkuje 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)
,P(X=x)
to prawie na pewno 0, dla wszystkich wartości x. (Jest tak, ponieważ w przedziale jest nieskończenie wiele możliwych wartości.) Jeśli szukasz dokładnie 0 lub 1, powinieneś użyć innej funkcji - na przykładrandom.choice