Inne odpowiedzi wskazywali, że wynik jest zawsze random()
ściśle mniej niż 1.0
; to jednak tylko połowa historii.
Jeśli obliczeniowej randrange(n)
jako int(random() * n)
, ty również muszą wiedzieć, że za każdym Python pływaka x
spełniającej 0.0 <= x < 1.0
i każda dodatnia n
, to prawda, że 0.0 <= x * n < n
, tak że int(x * n)
jest ściśle mniej niż n
.
Są dwie rzeczy, które mogą pójść źle: po pierwsze, kiedy obliczyć x * n
, n
jest niejawnie konwertowane na float. Dla wystarczająco dużych n
, ta konwersja może zmienić wartość.Ale jeśli spojrzysz na źródło Pythona, zobaczysz, że używa on tylko metody int(random() * n)
dla mniejszej niż (tutaj i poniżej zakładam, że platforma używa IEEE 754), która jest zakresem, w którym konwersja n
na pływak gwarantuje się, że nie straci informacji (ponieważ n
może być reprezentowany dokładnie tak jak float).
Drugą rzeczą, która może się nie udać, jest to, że wynik mnożenia x * n
(który obecnie jest wykonywany jako efekt pływaków, pamiętaj) prawdopodobnie nie będzie dokładnie reprezentowany, więc będzie zaistnieć pewne zaokrąglenie. Jeśli wartość parametru x
jest wystarczająco zbliżona do wartości 1.0
, możliwe jest, że zaokrąglenie obejmie wynik równy wartości n
.
Aby zobaczyć, że tak się nie stanie, musimy wziąć pod uwagę tylko największą możliwą wartość dla x
, która jest (na prawie wszystkich komputerach, na których działa Python) 1 - 2**-53
. Musimy więc pokazać, że (1 - 2**-53) * n < n
dla naszej pozytywnej liczby całkowitej n
, ponieważ zawsze będzie prawdą, że random() * n <= (1 - 2**-53) * n
.
Dowód (schemat) Niech k
być unikalną k
liczbą całkowitą tak, że 2**(k-1) < n <= 2**k
. Następnie następna zmiana z n
to n - 2**(k-53)
. Musimy pokazać, że n*(1-2**53)
(tj. Rzeczywista, niezaokrąglona wartość produktu) jest bliższa n - 2**(k-53)
niż n
, więc zawsze będzie zaokrąglana w dół. Ale mała arytmetyka pokazuje, że odległość od n*(1-2**-53)
do n
wynosi 2**-53 * n
, natomiast odległość od n*(1-2**-53)
do n - 2**(k-53)
to (2**k - n) * 2**-53
. Ale 2**k - n < n
(ponieważ wybraliśmy k
więc tym 2**(k-1) < n
), dzięki czemu produkt jest bliżej n - 2**(k-53)
, więc będzie się zaokrągla się w dół (przy założeniu, że to, że platforma robi jakąś formę okrągły do najbliższego) .
Jesteśmy więc bezpieczni. Uff!
Dodatek (4.7.2015) Powyższy zakłada IEEE 754 binary64 arytmetycznych, z okrągłym-tie-do-parzystym trybie zaokrąglania. Na wielu maszynach to założenie jest dość bezpieczne. Jednak na maszynach x86, które używają FPU x87 dla zmiennoprzecinkowej (na przykład różne smaki 32-bitowego systemu Linux), istnieje możliwość double rounding w mnożeniu, co umożliwia random() * n
okrążenie w górę na n
w przypadku, gdy random()
zwraca największą możliwą wartość. Najmniejszy taki n
, dla którego może się to zdarzyć, to n = 2049
. Zobacz dyskusję pod numerem http://bugs.python.org/issue24546, aby uzyskać więcej informacji.
Nawiasem mówiąc, 'int (random.random() * n)' nadal nie jest idealnym sposobem na generowanie liczb całkowitych, które są równomiernie rozmieszczone w 'zakresie (n)'; istnieje odchylenie, które jest nieistotne dla małego 'n', ale staje się znaczące gdy' n' staje się duże. Otworzyłem błąd w języku Python na http://bugs.python.org/issue9025 –
@ Mark Dickinson: Dzięki! To fascynujące. –
@ Mark Dickinson: [Ten błąd został naprawiony od dzisiaj] (http://docs.python.org/dev/whatsnew/3.2.html#random). –