[Python-checkins] Add recipe for a version of random() with a larger population (GH-22664)
Raymond Hettinger
webhook-mailer at python.org
Tue Oct 13 14:54:31 EDT 2020
https://github.com/python/cpython/commit/8b2ff4c03d150c43df3e8438d323b7f7bfe3353c
commit: 8b2ff4c03d150c43df3e8438d323b7f7bfe3353c
branch: master
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2020-10-13T11:54:21-07:00
summary:
Add recipe for a version of random() with a larger population (GH-22664)
files:
M Doc/library/random.rst
diff --git a/Doc/library/random.rst b/Doc/library/random.rst
index af5131df280c2..c19a8e0a7cb5b 100644
--- a/Doc/library/random.rst
+++ b/Doc/library/random.rst
@@ -391,8 +391,8 @@ change across Python versions, but two aspects are guaranteed not to change:
.. _random-examples:
-Examples and Recipes
---------------------
+Examples
+--------
Basic examples::
@@ -516,6 +516,52 @@ Simulation of arrival times and service deliveries for a multiserver queue::
print(f'Mean wait: {mean(waits):.1f} Max wait: {max(waits):.1f}')
print('Quartiles:', [round(q, 1) for q in quantiles(waits)])
+Recipes
+-------
+
+The default :func:`.random` returns multiples of 2⁻⁵³ in the range
+*0.0 ≤ x < 1.0*. All such numbers are evenly spaced and exactly
+representable as Python floats. However, many floats in that interval
+are not possible selections. For example, ``0.05954861408025609``
+isn't an integer multiple of 2⁻⁵³.
+
+The following recipe takes a different approach. All floats in the
+interval are possible selections. Conceptually it works by choosing
+from evenly spaced multiples of 2⁻¹⁰⁷⁴ and then rounding down to the
+nearest representable float.
+
+For efficiency, the actual mechanics involve calling
+:func:`~math.ldexp` to construct a representable float. The mantissa
+comes from a uniform distribution of integers in the range *2⁵² ≤
+mantissa < 2⁵³*. The exponent comes from a geometric distribution
+where exponents smaller than *-53* occur half as often as the next
+larger exponent.
+
+::
+
+ from random import Random
+ from math import ldexp
+
+ class FullRandom(Random):
+
+ def random(self):
+ mantissa = 0x10_0000_0000_0000 | self.getrandbits(52)
+ exponent = -53
+ x = 0
+ while not x:
+ x = self.getrandbits(32)
+ exponent += x.bit_length() - 32
+ return ldexp(mantissa, exponent)
+
+All of the real valued distributions will use the new method::
+
+ >>> fr = FullRandom()
+ >>> fr.random()
+ 0.05954861408025609
+ >>> fr.expovariate(0.25)
+ 8.87925541791544
+
+
.. seealso::
`Statistics for Hackers <https://www.youtube.com/watch?v=Iq9DzN6mvYA>`_
@@ -536,3 +582,8 @@ Simulation of arrival times and service deliveries for a multiserver queue::
a tutorial by `Peter Norvig <http://norvig.com/bio.html>`_ covering
the basics of probability theory, how to write simulations, and
how to perform data analysis using Python.
+
+ `Generating Pseudo-random Floating-Point Values
+ <https://allendowney.com/research/rand/downey07randfloat.pdf>`_ a
+ paper by Allen B. Downey describing ways to generate more
+ fine-grained floats than normally generated by :func:`.random`.
More information about the Python-checkins
mailing list