Improve recipe readability (GH-22685) (GH-22686)

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

View file

@ -253,6 +253,8 @@ Functions for sequences
order so that the sample is reproducible. order so that the sample is reproducible.
.. _real-valued-distributions:
Real-valued distributions Real-valued distributions
------------------------- -------------------------
@ -516,52 +518,6 @@ 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>`_
@ -583,6 +539,56 @@ All of the real valued distributions will use the new method::
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.
Recipes
-------
The default :func:`.random` returns multiples of 2⁻⁵³ in the range
*0.0 ≤ x < 1.0*. All such numbers are evenly spaced and are 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. 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 :ref:`real valued distributions <real-valued-distributions>`
in the class will use the new method::
>>> fr = FullRandom()
>>> fr.random()
0.05954861408025609
>>> fr.expovariate(0.25)
8.87925541791544
The recipe is conceptually equivalent to an algorithm that chooses from
all the multiples of 2⁻¹⁰⁷⁴ in the range *0.0 ≤ x < 1.0*. All such
numbers are evenly spaced, but most have to be rounded down to the
nearest representable Python float. (The value 2⁻¹⁰⁷⁴ is the smallest
positive unnormalized float and is equal to ``math.ulp(0.0)``.)
.. seealso::
`Generating Pseudo-random Floating-Point Values `Generating Pseudo-random Floating-Point Values
<https://allendowney.com/research/rand/downey07randfloat.pdf>`_ a <https://allendowney.com/research/rand/downey07randfloat.pdf>`_ a
paper by Allen B. Downey describing ways to generate more paper by Allen B. Downey describing ways to generate more