From bbfbe206e81299f5c21fdb34e66e784a5fa61e2f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 21 Jun 2020 23:59:55 -0700 Subject: [PATCH 1/4] Small optimization: floor() is faster and clearer than int() --- Lib/random.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index 02a56c6935b893..6d82b562af01ce 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -39,7 +39,8 @@ 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, 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 _collections_abc import Set as _Set, Sequence as _Sequence from itertools import accumulate as _accumulate, repeat as _repeat @@ -307,7 +308,7 @@ def _randbelow_without_getrandbits(self, n, int=int, maxsize=1<= limit: r = random() - return int(r * maxsize) % n + return _floor(r * maxsize) % n _randbelow = _randbelow_with_getrandbits @@ -346,10 +347,10 @@ def shuffle(self, x, random=None): 'since Python 3.9 and will be removed in a subsequent ' 'version.', DeprecationWarning, 2) - _int = int + floor = _floor 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 = floor(random() * (i + 1)) x[i], x[j] = x[j], x[i] def sample(self, population, k, *, counts=None): @@ -462,9 +463,9 @@ def choices(self, population, weights=None, *, cum_weights=None, k=1): n = len(population) if cum_weights is None: if weights is None: - _int = int + floor = _floor 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)) elif weights is not None: raise TypeError('Cannot specify both weights and cumulative weights') From cc88e9338e1f58c3b571b0ed3c63c694ff2ad7f5 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 22 Jun 2020 11:58:46 -0700 Subject: [PATCH 2/4] Remove default argument for _int=int --- Lib/random.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index 6d82b562af01ce..ccb029f6915036 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -235,7 +235,7 @@ def __reduce__(self): ## -------------------- 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]). This fixes the problem with randint() which includes the @@ -245,7 +245,7 @@ def randrange(self, start, stop=None, step=1, _int=int): # This code is a bit messy to make it fast for the # common case while still doing adequate error checking. - istart = _int(start) + istart = int(start) if istart != start: raise ValueError("non-integer arg 1 for randrange()") if stop is None: @@ -254,7 +254,7 @@ def randrange(self, start, stop=None, step=1, _int=int): raise ValueError("empty range for randrange()") # stop argument supplied. - istop = _int(stop) + istop = int(stop) if istop != stop: raise ValueError("non-integer stop for randrange()") width = istop - istart @@ -264,7 +264,7 @@ def randrange(self, start, stop=None, step=1, _int=int): raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) # Non-unit step argument supplied. - istep = _int(step) + istep = int(step) if istep != step: raise ValueError("non-integer step for randrange()") if istep > 0: From 126e4a9f884dc324597bc4bc62fbef9e9ba2fbd1 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 22 Jun 2020 12:09:51 -0700 Subject: [PATCH 3/4] Remove unused default argument --- Lib/random.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/random.py b/Lib/random.py index ccb029f6915036..c43cd2ef7f3b11 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -297,7 +297,7 @@ def _randbelow_with_getrandbits(self, n): r = getrandbits(k) return r - def _randbelow_without_getrandbits(self, n, int=int, maxsize=1< Date: Mon, 22 Jun 2020 12:40:45 -0700 Subject: [PATCH 4/4] Clean-up test generator code: * Timings should only include data generation and exclude analysis * Can now use the statistics library * Use f-strings for formatting --- Lib/random.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/Lib/random.py b/Lib/random.py index c43cd2ef7f3b11..ae7b5cf4e72e87 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -815,24 +815,20 @@ def _notimplemented(self, *args, **kwds): ## -------------------- test program -------------------- def _test_generator(n, func, args): - import time - print(n, 'times', func.__name__) - total = 0.0 - sqsum = 0.0 - smallest = 1e10 - largest = -1e10 - t0 = time.perf_counter() - for i in range(n): - x = func(*args) - total += x - sqsum = sqsum + x*x - 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)) + from statistics import stdev, fmean as mean + from time import perf_counter + + t0 = perf_counter() + data = [func(*args) for i in range(n)] + t1 = perf_counter() + + xbar = mean(data) + sigma = stdev(data, xbar) + low = min(data) + high = max(data) + + print(f'{t1 - t0:.3f} sec, {n} times {func.__name__}') + print('avg %g, stddev %g, min %g, max %g\n' % (xbar, sigma, low, high)) def _test(N=2000):