summaryrefslogtreecommitdiffstats
path: root/Lib/random.py
diff options
context:
space:
mode:
authorRaymond Hettinger <rhettinger@users.noreply.github.com>2020-06-13 16:46:47 (GMT)
committerGitHub <noreply@github.com>2020-06-13 16:46:47 (GMT)
commit9db5b8d44858d134b0b225df481b784d7511dbd4 (patch)
tree864c56201e4b247119b9a4d0dd7422b2b2b6a922 /Lib/random.py
parent9672912e8f90374fd31b37ca0fb7cefbc6f4c555 (diff)
downloadcpython-9db5b8d44858d134b0b225df481b784d7511dbd4.zip
cpython-9db5b8d44858d134b0b225df481b784d7511dbd4.tar.gz
cpython-9db5b8d44858d134b0b225df481b784d7511dbd4.tar.bz2
Minor code clean-ups (GH-20838)
Diffstat (limited to 'Lib/random.py')
-rw-r--r--Lib/random.py199
1 files changed, 110 insertions, 89 deletions
diff --git a/Lib/random.py b/Lib/random.py
index 75f70d5..02a56c6 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -39,7 +39,7 @@ General notes on the underlying Mersenne Twister core generator:
from warnings import warn as _warn
from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil
-from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin
+from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin, tau as TWOPI
from os import urandom as _urandom
from _collections_abc import Set as _Set, Sequence as _Sequence
from itertools import accumulate as _accumulate, repeat as _repeat
@@ -54,19 +54,38 @@ except ImportError:
from hashlib import sha512 as _sha512
-__all__ = ["Random","seed","random","uniform","randint","choice","sample",
- "randrange","shuffle","normalvariate","lognormvariate",
- "expovariate","vonmisesvariate","gammavariate","triangular",
- "gauss","betavariate","paretovariate","weibullvariate",
- "getstate","setstate", "getrandbits", "choices",
- "SystemRandom"]
-
-NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0)
-TWOPI = 2.0*_pi
+__all__ = [
+ "Random",
+ "SystemRandom",
+ "betavariate",
+ "choice",
+ "choices",
+ "expovariate",
+ "gammavariate",
+ "gauss",
+ "getrandbits",
+ "getstate",
+ "lognormvariate",
+ "normalvariate",
+ "paretovariate",
+ "randint",
+ "random",
+ "randrange",
+ "sample",
+ "seed",
+ "setstate",
+ "shuffle",
+ "triangular",
+ "uniform",
+ "vonmisesvariate",
+ "weibullvariate",
+]
+
+NV_MAGICCONST = 4 * _exp(-0.5) / _sqrt(2.0)
LOG4 = _log(4.0)
SG_MAGICCONST = 1.0 + _log(4.5)
BPF = 53 # Number of bits in a float
-RECIP_BPF = 2**-BPF
+RECIP_BPF = 2 ** -BPF
# Translated by Guido van Rossum from C source provided by
@@ -75,6 +94,7 @@ RECIP_BPF = 2**-BPF
import _random
+
class Random(_random.Random):
"""Random number generator base class used by bound module functions.
@@ -180,7 +200,7 @@ class Random(_random.Random):
# really unsigned 32-bit ints, so we convert negative ints from
# version 2 to positive longs for version 3.
try:
- internalstate = tuple(x % (2**32) for x in internalstate)
+ internalstate = tuple(x % (2 ** 32) for x in internalstate)
except ValueError as e:
raise TypeError from e
super().setstate(internalstate)
@@ -189,21 +209,21 @@ class Random(_random.Random):
"Random.setstate() of version %s" %
(version, self.VERSION))
-## ---- Methods below this point do not need to be overridden when
-## ---- subclassing for the purpose of using a different core generator.
+ ## ---- Methods below this point do not need to be overridden when
+ ## ---- subclassing for the purpose of using a different core generator.
-## -------------------- bytes methods ---------------------
+ ## -------------------- bytes methods ---------------------
def randbytes(self, n):
"""Generate n random bytes."""
return self.getrandbits(n * 8).to_bytes(n, 'little')
-## -------------------- pickle support -------------------
+ ## -------------------- pickle support -------------------
# Issue 17489: Since __reduce__ was defined to fix #759889 this is no
# longer called; we leave it here because it has been here since random was
# rewritten back in 2001 and why risk breaking something.
- def __getstate__(self): # for pickle
+ def __getstate__(self): # for pickle
return self.getstate()
def __setstate__(self, state): # for pickle
@@ -212,7 +232,7 @@ class Random(_random.Random):
def __reduce__(self):
return self.__class__, (), self.getstate()
-## -------------------- integer methods -------------------
+ ## -------------------- integer methods -------------------
def randrange(self, start, stop=None, step=1, _int=int):
"""Choose a random item from range(start, stop[, step]).
@@ -256,7 +276,7 @@ class Random(_random.Random):
if n <= 0:
raise ValueError("empty range for randrange()")
- return istart + istep*self._randbelow(n)
+ return istart + istep * self._randbelow(n)
def randint(self, a, b):
"""Return random integer in range [a, b], including both end points.
@@ -271,7 +291,7 @@ class Random(_random.Random):
return 0
getrandbits = self.getrandbits
k = n.bit_length() # don't use (n-1) here because n can be 1
- r = getrandbits(k) # 0 <= r < 2**k
+ r = getrandbits(k) # 0 <= r < 2**k
while r >= n:
r = getrandbits(k)
return r
@@ -295,15 +315,16 @@ class Random(_random.Random):
r = random()
while r >= limit:
r = random()
- return int(r*maxsize) % n
+ return int(r * maxsize) % n
_randbelow = _randbelow_with_getrandbits
-## -------------------- sequence methods -------------------
+ ## -------------------- sequence methods -------------------
def choice(self, seq):
"""Choose a random element from a non-empty sequence."""
- return seq[self._randbelow(len(seq))] # raises IndexError if seq is empty
+ # raises IndexError if seq is empty
+ return seq[self._randbelow(len(seq))]
def shuffle(self, x, random=None):
"""Shuffle list x in place, and return None.
@@ -318,7 +339,7 @@ class Random(_random.Random):
randbelow = self._randbelow
for i in reversed(range(1, len(x))):
# pick an element in x[:i+1] with which to exchange x[i]
- j = randbelow(i+1)
+ j = randbelow(i + 1)
x[i], x[j] = x[j], x[i]
else:
_warn('The *random* parameter to shuffle() has been deprecated\n'
@@ -328,7 +349,7 @@ class Random(_random.Random):
_int = int
for i in reversed(range(1, len(x))):
# pick an element in x[:i+1] with which to exchange x[i]
- j = _int(random() * (i+1))
+ j = _int(random() * (i + 1))
x[i], x[j] = x[j], x[i]
def sample(self, population, k, *, counts=None):
@@ -410,14 +431,15 @@ class Random(_random.Random):
result = [None] * k
setsize = 21 # size of a small set minus size of an empty list
if k > 5:
- setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets
+ setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets
if n <= setsize:
- # An n-length list is smaller than a k-length set
+ # An n-length list is smaller than a k-length set.
+ # Invariant: non-selected at pool[0 : n-i]
pool = list(population)
- for i in range(k): # invariant: non-selected at [0,n-i)
- j = randbelow(n-i)
+ for i in range(k):
+ j = randbelow(n - i)
result[i] = pool[j]
- pool[j] = pool[n-i-1] # move non-selected item into vacancy
+ pool[j] = pool[n - i - 1] # move non-selected item into vacancy
else:
selected = set()
selected_add = selected.add
@@ -456,15 +478,15 @@ class Random(_random.Random):
return [population[bisect(cum_weights, random() * total, 0, hi)]
for i in _repeat(None, k)]
-## -------------------- real-valued distributions -------------------
+ ## -------------------- real-valued distributions -------------------
-## -------------------- uniform distribution -------------------
+ ## -------------------- uniform distribution -------------------
def uniform(self, a, b):
"Get a random number in the range [a, b) or [a, b] depending on rounding."
- return a + (b-a) * self.random()
+ return a + (b - a) * self.random()
-## -------------------- triangular --------------------
+ ## -------------------- triangular --------------------
def triangular(self, low=0.0, high=1.0, mode=None):
"""Triangular distribution.
@@ -486,7 +508,7 @@ class Random(_random.Random):
low, high = high, low
return low + (high - low) * _sqrt(u * c)
-## -------------------- normal distribution --------------------
+ ## -------------------- normal distribution --------------------
def normalvariate(self, mu, sigma):
"""Normal distribution.
@@ -502,16 +524,16 @@ class Random(_random.Random):
# Math Software, 3, (1977), pp257-260.
random = self.random
- while 1:
+ while True:
u1 = random()
u2 = 1.0 - random()
- z = NV_MAGICCONST*(u1-0.5)/u2
- zz = z*z/4.0
+ z = NV_MAGICCONST * (u1 - 0.5) / u2
+ zz = z * z / 4.0
if zz <= -_log(u2):
break
- return mu + z*sigma
+ return mu + z * sigma
-## -------------------- lognormal distribution --------------------
+ ## -------------------- lognormal distribution --------------------
def lognormvariate(self, mu, sigma):
"""Log normal distribution.
@@ -523,7 +545,7 @@ class Random(_random.Random):
"""
return _exp(self.normalvariate(mu, sigma))
-## -------------------- exponential distribution --------------------
+ ## -------------------- exponential distribution --------------------
def expovariate(self, lambd):
"""Exponential distribution.
@@ -540,9 +562,9 @@ class Random(_random.Random):
# we use 1-random() instead of random() to preclude the
# possibility of taking the log of zero.
- return -_log(1.0 - self.random())/lambd
+ return -_log(1.0 - self.random()) / lambd
-## -------------------- von Mises distribution --------------------
+ ## -------------------- von Mises distribution --------------------
def vonmisesvariate(self, mu, kappa):
"""Circular data distribution.
@@ -571,7 +593,7 @@ class Random(_random.Random):
s = 0.5 / kappa
r = s + _sqrt(1.0 + s * s)
- while 1:
+ while True:
u1 = random()
z = _cos(_pi * u1)
@@ -590,7 +612,7 @@ class Random(_random.Random):
return theta
-## -------------------- gamma distribution --------------------
+ ## -------------------- gamma distribution --------------------
def gammavariate(self, alpha, beta):
"""Gamma distribution. Not the gamma function!
@@ -625,32 +647,31 @@ class Random(_random.Random):
while 1:
u1 = random()
- if not 1e-7 < u1 < .9999999:
+ if not 1e-7 < u1 < 0.9999999:
continue
u2 = 1.0 - random()
- v = _log(u1/(1.0-u1))/ainv
- x = alpha*_exp(v)
- z = u1*u1*u2
- r = bbb+ccc*v-x
- if r + SG_MAGICCONST - 4.5*z >= 0.0 or r >= _log(z):
+ v = _log(u1 / (1.0 - u1)) / ainv
+ x = alpha * _exp(v)
+ z = u1 * u1 * u2
+ r = bbb + ccc * v - x
+ if r + SG_MAGICCONST - 4.5 * z >= 0.0 or r >= _log(z):
return x * beta
elif alpha == 1.0:
# expovariate(1/beta)
return -_log(1.0 - random()) * beta
- else: # alpha is between 0 and 1 (exclusive)
-
+ else:
+ # alpha is between 0 and 1 (exclusive)
# Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle
-
- while 1:
+ while True:
u = random()
- b = (_e + alpha)/_e
- p = b*u
+ b = (_e + alpha) / _e
+ p = b * u
if p <= 1.0:
- x = p ** (1.0/alpha)
+ x = p ** (1.0 / alpha)
else:
- x = -_log((b-p)/alpha)
+ x = -_log((b - p) / alpha)
u1 = random()
if p > 1.0:
if u1 <= x ** (alpha - 1.0):
@@ -659,7 +680,7 @@ class Random(_random.Random):
break
return x * beta
-## -------------------- Gauss (faster alternative) --------------------
+ ## -------------------- Gauss (faster alternative) --------------------
def gauss(self, mu, sigma):
"""Gaussian distribution.
@@ -698,21 +719,21 @@ class Random(_random.Random):
z = _cos(x2pi) * g2rad
self.gauss_next = _sin(x2pi) * g2rad
- return mu + z*sigma
-
-## -------------------- beta --------------------
-## See
-## http://mail.python.org/pipermail/python-bugs-list/2001-January/003752.html
-## for Ivan Frohne's insightful analysis of why the original implementation:
-##
-## def betavariate(self, alpha, beta):
-## # Discrete Event Simulation in C, pp 87-88.
-##
-## y = self.expovariate(alpha)
-## z = self.expovariate(1.0/beta)
-## return z/(y+z)
-##
-## was dead wrong, and how it probably got that way.
+ return mu + z * sigma
+
+ ## -------------------- beta --------------------
+ ## See
+ ## http://mail.python.org/pipermail/python-bugs-list/2001-January/003752.html
+ ## for Ivan Frohne's insightful analysis of why the original implementation:
+ ##
+ ## def betavariate(self, alpha, beta):
+ ## # Discrete Event Simulation in C, pp 87-88.
+ ##
+ ## y = self.expovariate(alpha)
+ ## z = self.expovariate(1.0/beta)
+ ## return z/(y+z)
+ ##
+ ## was dead wrong, and how it probably got that way.
def betavariate(self, alpha, beta):
"""Beta distribution.
@@ -725,21 +746,20 @@ class Random(_random.Random):
# This version due to Janne Sinkkonen, and matches all the std
# texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution").
y = self.gammavariate(alpha, 1.0)
- if y == 0:
- return 0.0
- else:
+ if y:
return y / (y + self.gammavariate(beta, 1.0))
+ return 0.0
-## -------------------- Pareto --------------------
+ ## -------------------- Pareto --------------------
def paretovariate(self, alpha):
"""Pareto distribution. alpha is the shape parameter."""
# Jain, pg. 495
u = 1.0 - self.random()
- return 1.0 / u ** (1.0/alpha)
+ return 1.0 / u ** (1.0 / alpha)
-## -------------------- Weibull --------------------
+ ## -------------------- Weibull --------------------
def weibullvariate(self, alpha, beta):
"""Weibull distribution.
@@ -750,7 +770,8 @@ class Random(_random.Random):
# Jain, pg. 499; bug fix courtesy Bill Arms
u = 1.0 - self.random()
- return alpha * (-_log(u)) ** (1.0/beta)
+ return alpha * (-_log(u)) ** (1.0 / beta)
+
## --------------- Operating System Random Source ------------------
@@ -789,6 +810,7 @@ class SystemRandom(Random):
raise NotImplementedError('System entropy source does not have state.')
getstate = setstate = _notimplemented
+
## -------------------- test program --------------------
def _test_generator(n, func, args):
@@ -806,11 +828,10 @@ def _test_generator(n, func, args):
smallest = min(x, smallest)
largest = max(x, largest)
t1 = time.perf_counter()
- print(round(t1-t0, 3), 'sec,', end=' ')
- avg = total/n
- stddev = _sqrt(sqsum/n - avg*avg)
- print('avg %g, stddev %g, min %g, max %g\n' % \
- (avg, stddev, smallest, largest))
+ print(round(t1 - t0, 3), 'sec,', end=' ')
+ avg = total / n
+ stddev = _sqrt(sqsum / n - avg * avg)
+ print('avg %g, stddev %g, min %g, max %g\n' % (avg, stddev, smallest, largest))
def _test(N=2000):
@@ -829,11 +850,11 @@ def _test(N=2000):
_test_generator(N, gammavariate, (200.0, 1.0))
_test_generator(N, gauss, (0.0, 1.0))
_test_generator(N, betavariate, (3.0, 3.0))
- _test_generator(N, triangular, (0.0, 1.0, 1.0/3.0))
+ _test_generator(N, triangular, (0.0, 1.0, 1.0 / 3.0))
# Create one instance, seeded from current time, and export its methods
# as module-level functions. The functions share state across all uses
-#(both in the user's code and in the Python libraries), but that's fine
+# (both in the user's code and in the Python libraries), but that's fine
# for most programs and is easier for the casual user than making them
# instantiate their own Random() instance.