Add recipe for a version of random() with a larger population (GH-22664) (GH-22684)

This commit is contained in:
Miss Skeleton (bot) 2020-10-13 12:38:13 -07:00 committed by GitHub
parent afe86066e7
commit 5f0007f0f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -391,8 +391,8 @@ change across Python versions, but two aspects are guaranteed not to change:
.. _random-examples: .. _random-examples:
Examples and Recipes Examples
-------------------- --------
Basic 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}. Stdev wait: {stdev(waits):.1f}.') print(f'Mean wait: {mean(waits):.1f}. Stdev wait: {stdev(waits):.1f}.')
print(f'Median wait: {median(waits):.1f}. Max wait: {max(waits):.1f}.') print(f'Median wait: {median(waits):.1f}. Max wait: {max(waits):.1f}.')
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:: .. seealso::
`Statistics for Hackers <https://www.youtube.com/watch?v=Iq9DzN6mvYA>`_ `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 a tutorial by `Peter Norvig <http://norvig.com/bio.html>`_ covering
the basics of probability theory, how to write simulations, and the basics of probability theory, how to write simulations, and
how to perform data analysis using Python. 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`.