Czy random.uniform (0,1) może kiedykolwiek wygenerować 0 lub 1?

9

W dokumentacji jest powiedziane, że istnieje szansa na uniform(0,1)wygenerowanie wartości 0i 1.

Uruchomiłem uniform(0, 1)10000 razy, ale nigdy nie wygenerowało zera. Nawet w przypadku uniform(0, 0.001).

Czy random.uniform(0,1)kiedykolwiek można wygenerować 0lub 1?

Venkatesh Gandi
źródło
3
Teoretycznie jest to możliwe, ale praktycznie nigdy się nie wydarzy. Matematycznie standardowa jednolita zmienna losowa może przyjąć dowolną wartość w przedziale od 0 do 1. jeśli 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
pault
1
@pault prawie na pewno ma bardzo konkretne znaczenie w matematyce, co tak naprawdę nie ma tutaj sensu, ponieważ mamy dyskretny rozkład, a nie ciągły przedział. Istnieje tylko skończona liczba liczb zmiennoprzecinkowych między 0 a 1.
wim
2
@pault Więc dlaczego mówisz matematycznie, kiedy OP pyta o wdrożenie random.uniform?
wim
1
Jeśli ta dokumentacja jest dokładna, jestem ciekawy, w jaki sposób jest ona wytwarzana zarówno do zera, jak i do 1. Wydaje się, że [0, 1) byłoby o wiele łatwiejsze ( Math.random()na przykład, jak działa w JavaScript).
Ry-
1
50 punktów nagrody za pierwszą osobę, która opublikuje losowy materiał siewny, co powoduje random.uniform(0, 1)zwrot 0 przy pierwszym połączeniu
wim

Odpowiedzi:

13

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 bmoż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ć 1dokładnie. To by się stało tylko, jeśli random.random()jest 1.0 exactly. However,losowo () *never* produces1.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. floatTyp 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ć bdla pewnych wartości ai b. Ale 0i 1nie należą do tych wartości.

Zauważ, że random.random() może produkować 0,0, więc ajest zawsze uwzględniany w możliwych wartościach dla random.uniform()( a + (b - a) * 0 == a). Ponieważ istnieją 2 ** 53róż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 - aaby 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 ai b, na przykład kiedy ai bmają 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.maxi 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
Martijn Pieters
źródło
5

„Kilka razy” to za mało. 10 000 to za mało. random.uniformwybiera spośród 2 ^ 53 (9 007,199,254,740,992) różnych wartości. Jesteś zainteresowany dwoma z nich. Jako taki, powinieneś spodziewać się wygenerowania kilku biliardów losowych wartości przed uzyskaniem wartości dokładnie 0 lub 1. Jest to możliwe, ale jest bardzo prawdopodobne, że nigdy jej nie zaobserwujesz.

Hobbs
źródło
2
Ponieważ uniform(0, 1)nie da się wyprodukować 1jako wyniku. Jest tak, ponieważ funkcja jest po prostu zdefiniowana jako def uniform(a, b): return a + (b - a) * random()i random()nigdy nie może wytworzyć 1.0.
Martijn Pieters
2
@MartijnPieters Uważam, że masz rację, i głosowałem za Twoją odpowiedzią. Podejrzewałem tyle samo, ale nie byłem pewien, i to było poza głównym celem mojej odpowiedzi, więc niech tak będzie :)
Hobbs
1

Możesz spróbować wygenerować pętlę, która zlicza liczbę iteracji potrzebnych do wyświetlenia dokładnego 0 (nie).

Ponadto, jak stwierdził Hobbs, liczba uniformlypróbkowanych wartości wynosi 9 007 199,254,740,992. Co oznacza, że ​​prawdopodobieństwo zobaczenia 0 wynosi dokładnie 1 / 9,007,199,254,740,992. Co w ujęciu ogólnym i zaokrąglanie w górę oznacza, że ​​będziesz potrzebować średnio 10 kwatrillionów próbek, aby znaleźć 0. Oczywiście, że możesz go znaleźć przy pierwszych 10 próbach lub nigdy.

Próbkowanie 1 jest niemożliwe, ponieważ przedział zdefiniowany dla wartości jest zamykany nawiasami, dlatego nie obejmuje 1.

Celius Stingher
źródło
1

Pewnie. Zamiast tego byłeś już na dobrej drodze uniform(0, 0.001). Po prostu ograniczaj granice na tyle, aby stało się to wcześniej.

>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0
wim
źródło