summaryrefslogtreecommitdiff
path: root/Lib/random.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/random.py')
-rw-r--r--Lib/random.py54
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