Skip to content

Commit

Permalink
Add recipe for a version of random() with a larger population (python…
Browse files Browse the repository at this point in the history
  • Loading branch information
rhettinger committed Oct 13, 2020
1 parent cfb0f57 commit 8b2ff4c
Showing 1 changed file with 53 additions and 2 deletions.
55 changes: 53 additions & 2 deletions Doc/library/random.rst
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,8 @@ change across Python versions, but two aspects are guaranteed not to change:

.. _random-examples:

Examples and Recipes
--------------------
Examples
--------

Basic examples::

Expand Down Expand Up @@ -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>`_
Expand All @@ -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`.

0 comments on commit 8b2ff4c

Please sign in to comment.