diff options
Diffstat (limited to 'Lib/random.py')
-rw-r--r-- | Lib/random.py | 54 |
1 files changed, 35 insertions, 19 deletions
diff --git a/Lib/random.py b/Lib/random.py index 4efbb0a2b9..49b0f149a5 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -8,6 +8,7 @@ --------- pick random element pick random sample + pick weighted random sample generate random permutation distributions on the real line: @@ -43,12 +44,14 @@ from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin from os import urandom as _urandom from _collections_abc import Set as _Set, Sequence as _Sequence from hashlib import sha512 as _sha512 +import itertools as _itertools +import bisect as _bisect __all__ = ["Random","seed","random","uniform","randint","choice","sample", "randrange","shuffle","normalvariate","lognormvariate", "expovariate","vonmisesvariate","gammavariate","triangular", "gauss","betavariate","paretovariate","weibullvariate", - "getstate","setstate", "getrandbits", + "getstate","setstate", "getrandbits", "choices", "SystemRandom"] NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) @@ -105,15 +108,6 @@ class Random(_random.Random): """ - if a is None: - try: - # Seed with enough bytes to span the 19937 bit - # state space for the Mersenne Twister - a = int.from_bytes(_urandom(2500), 'big') - except NotImplementedError: - import time - a = int(time.time() * 256) # use fractional seconds - if version == 1 and isinstance(a, (str, bytes)): x = ord(a[0]) << 7 if a else 0 for c in a: @@ -121,12 +115,11 @@ class Random(_random.Random): x ^= len(a) a = -2 if x == -1 else x - if version == 2: - if isinstance(a, (str, bytes, bytearray)): - if isinstance(a, str): - a = a.encode() - a += _sha512(a).digest() - a = int.from_bytes(a, 'big') + if version == 2 and isinstance(a, (str, bytes, bytearray)): + if isinstance(a, str): + a = a.encode() + a += _sha512(a).digest() + a = int.from_bytes(a, 'big') super().seed(a) self.gauss_next = None @@ -321,7 +314,7 @@ class Random(_random.Random): randbelow = self._randbelow n = len(population) if not 0 <= k <= n: - raise ValueError("Sample larger than population") + raise ValueError("Sample larger than population or is negative") result = [None] * k setsize = 21 # size of a small set minus size of an empty list if k > 5: @@ -344,6 +337,28 @@ class Random(_random.Random): result[i] = population[j] return result + def choices(self, population, weights=None, *, cum_weights=None, k=1): + """Return a k sized list of population elements chosen with replacement. + + If the relative weights or cumulative weights are not specified, + the selections are made with equal probability. + + """ + random = self.random + if cum_weights is None: + if weights is None: + _int = int + total = len(population) + return [population[_int(random() * total)] for i in range(k)] + cum_weights = list(_itertools.accumulate(weights)) + elif weights is not None: + raise TypeError('Cannot specify both weights and cumulative weights') + if len(cum_weights) != len(population): + raise ValueError('The number of weights does not match the population') + bisect = _bisect.bisect + total = cum_weights[-1] + return [population[bisect(cum_weights, random() * total)] for i in range(k)] + ## -------------------- real-valued distributions ------------------- ## -------------------- uniform distribution ------------------- @@ -615,11 +630,11 @@ 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.) + y = self.gammavariate(alpha, 1.0) if y == 0: return 0.0 else: - return y / (y + self.gammavariate(beta, 1.)) + return y / (y + self.gammavariate(beta, 1.0)) ## -------------------- Pareto -------------------- @@ -734,6 +749,7 @@ choice = _inst.choice randrange = _inst.randrange sample = _inst.sample shuffle = _inst.shuffle +choices = _inst.choices normalvariate = _inst.normalvariate lognormvariate = _inst.lognormvariate expovariate = _inst.expovariate |