From f2bd04f6891071742eb000edde1ca81590349103 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 13 Oct 2020 16:41:26 -0700 Subject: [PATCH] Improve recipe readability (GH-22685) --- Doc/library/random.rst | 98 ++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/Doc/library/random.rst b/Doc/library/random.rst index c19a8e0a7cb..635f9e1c032 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -253,6 +253,8 @@ Functions for sequences order so that the sample is reproducible. +.. _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} 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 `_ @@ -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 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 ` +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 `_ a paper by Allen B. Downey describing ways to generate more