Small clean-ups for the random module (GH-21038)

This commit is contained in:
Raymond Hettinger 2020-06-22 19:38:59 -07:00 committed by GitHub
parent a16d697049
commit 26a1ad1c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 27 additions and 30 deletions

View File

@ -39,7 +39,8 @@ General notes on the underlying Mersenne Twister core generator:
from warnings import warn as _warn 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 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, tau as TWOPI from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin
from math import tau as TWOPI, floor as _floor
from os import urandom as _urandom from os import urandom as _urandom
from _collections_abc import Set as _Set, Sequence as _Sequence from _collections_abc import Set as _Set, Sequence as _Sequence
from itertools import accumulate as _accumulate, repeat as _repeat from itertools import accumulate as _accumulate, repeat as _repeat
@ -234,7 +235,7 @@ class Random(_random.Random):
## -------------------- integer methods ------------------- ## -------------------- integer methods -------------------
def randrange(self, start, stop=None, step=1, _int=int): def randrange(self, start, stop=None, step=1):
"""Choose a random item from range(start, stop[, step]). """Choose a random item from range(start, stop[, step]).
This fixes the problem with randint() which includes the This fixes the problem with randint() which includes the
@ -244,7 +245,7 @@ class Random(_random.Random):
# This code is a bit messy to make it fast for the # This code is a bit messy to make it fast for the
# common case while still doing adequate error checking. # common case while still doing adequate error checking.
istart = _int(start) istart = int(start)
if istart != start: if istart != start:
raise ValueError("non-integer arg 1 for randrange()") raise ValueError("non-integer arg 1 for randrange()")
if stop is None: if stop is None:
@ -253,7 +254,7 @@ class Random(_random.Random):
raise ValueError("empty range for randrange()") raise ValueError("empty range for randrange()")
# stop argument supplied. # stop argument supplied.
istop = _int(stop) istop = int(stop)
if istop != stop: if istop != stop:
raise ValueError("non-integer stop for randrange()") raise ValueError("non-integer stop for randrange()")
width = istop - istart width = istop - istart
@ -263,7 +264,7 @@ class Random(_random.Random):
raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
# Non-unit step argument supplied. # Non-unit step argument supplied.
istep = _int(step) istep = int(step)
if istep != step: if istep != step:
raise ValueError("non-integer step for randrange()") raise ValueError("non-integer step for randrange()")
if istep > 0: if istep > 0:
@ -296,7 +297,7 @@ class Random(_random.Random):
r = getrandbits(k) r = getrandbits(k)
return r return r
def _randbelow_without_getrandbits(self, n, int=int, maxsize=1<<BPF): def _randbelow_without_getrandbits(self, n, maxsize=1<<BPF):
"""Return a random int in the range [0,n). Returns 0 if n==0. """Return a random int in the range [0,n). Returns 0 if n==0.
The implementation does not use getrandbits, but only random. The implementation does not use getrandbits, but only random.
@ -307,7 +308,7 @@ class Random(_random.Random):
_warn("Underlying random() generator does not supply \n" _warn("Underlying random() generator does not supply \n"
"enough bits to choose from a population range this large.\n" "enough bits to choose from a population range this large.\n"
"To remove the range limitation, add a getrandbits() method.") "To remove the range limitation, add a getrandbits() method.")
return int(random() * n) return _floor(random() * n)
if n == 0: if n == 0:
return 0 return 0
rem = maxsize % n rem = maxsize % n
@ -315,7 +316,7 @@ class Random(_random.Random):
r = random() r = random()
while r >= limit: while r >= limit:
r = random() r = random()
return int(r * maxsize) % n return _floor(r * maxsize) % n
_randbelow = _randbelow_with_getrandbits _randbelow = _randbelow_with_getrandbits
@ -346,10 +347,10 @@ class Random(_random.Random):
'since Python 3.9 and will be removed in a subsequent ' 'since Python 3.9 and will be removed in a subsequent '
'version.', 'version.',
DeprecationWarning, 2) DeprecationWarning, 2)
_int = int floor = _floor
for i in reversed(range(1, len(x))): for i in reversed(range(1, len(x))):
# pick an element in x[:i+1] with which to exchange x[i] # pick an element in x[:i+1] with which to exchange x[i]
j = _int(random() * (i + 1)) j = floor(random() * (i + 1))
x[i], x[j] = x[j], x[i] x[i], x[j] = x[j], x[i]
def sample(self, population, k, *, counts=None): def sample(self, population, k, *, counts=None):
@ -462,9 +463,9 @@ class Random(_random.Random):
n = len(population) n = len(population)
if cum_weights is None: if cum_weights is None:
if weights is None: if weights is None:
_int = int floor = _floor
n += 0.0 # convert to float for a small speed improvement n += 0.0 # convert to float for a small speed improvement
return [population[_int(random() * n)] for i in _repeat(None, k)] return [population[floor(random() * n)] for i in _repeat(None, k)]
cum_weights = list(_accumulate(weights)) cum_weights = list(_accumulate(weights))
elif weights is not None: elif weights is not None:
raise TypeError('Cannot specify both weights and cumulative weights') raise TypeError('Cannot specify both weights and cumulative weights')
@ -814,24 +815,20 @@ class SystemRandom(Random):
## -------------------- test program -------------------- ## -------------------- test program --------------------
def _test_generator(n, func, args): def _test_generator(n, func, args):
import time from statistics import stdev, fmean as mean
print(n, 'times', func.__name__) from time import perf_counter
total = 0.0
sqsum = 0.0 t0 = perf_counter()
smallest = 1e10 data = [func(*args) for i in range(n)]
largest = -1e10 t1 = perf_counter()
t0 = time.perf_counter()
for i in range(n): xbar = mean(data)
x = func(*args) sigma = stdev(data, xbar)
total += x low = min(data)
sqsum = sqsum + x*x high = max(data)
smallest = min(x, smallest)
largest = max(x, largest) print(f'{t1 - t0:.3f} sec, {n} times {func.__name__}')
t1 = time.perf_counter() print('avg %g, stddev %g, min %g, max %g\n' % (xbar, sigma, low, high))
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): def _test(N=2000):