summaryrefslogtreecommitdiff
path: root/Lib/random.py
diff options
context:
space:
mode:
authorEzio Melotti <ezio.melotti@gmail.com>2011-04-15 08:27:00 +0300
committerEzio Melotti <ezio.melotti@gmail.com>2011-04-15 08:27:00 +0300
commit1896e4dc2b5e125961958b5240c50c7e8294a460 (patch)
treeb5bf87da152406ae4eb02e11a5826040800171b7 /Lib/random.py
parent3c6e0f6e68b357ec1eb4d7896ec66d714fc84a46 (diff)
parent86a82473160b5445dc845d52dc9382c8c03993d9 (diff)
downloadcpython-1896e4dc2b5e125961958b5240c50c7e8294a460.tar.gz
#11848: Merge with 3.1.
Diffstat (limited to 'Lib/random.py')
-rw-r--r--Lib/random.py137
1 files changed, 66 insertions, 71 deletions
diff --git a/Lib/random.py b/Lib/random.py
index 904e7c6a62..6bdd439b32 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -42,8 +42,8 @@ from types import MethodType as _MethodType, BuiltinMethodType as _BuiltinMethod
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 os import urandom as _urandom
-from binascii import hexlify as _hexlify
import collections as _collections
+from hashlib import sha512 as _sha512
__all__ = ["Random","seed","random","uniform","randint","choice","sample",
"randrange","shuffle","normalvariate","lognormvariate",
@@ -91,22 +91,33 @@ class Random(_random.Random):
self.seed(x)
self.gauss_next = None
- def seed(self, a=None):
+ def seed(self, a=None, version=2):
"""Initialize internal state from hashable object.
None or no argument seeds from current time or from an operating
system specific randomness source if available.
- If a is not None or an int, hash(a) is used instead.
+ For version 2 (the default), all of the bits are used if *a *is a str,
+ bytes, or bytearray. For version 1, the hash() of *a* is used instead.
+
+ If *a* is an int, all bits are used.
+
"""
if a is None:
try:
- a = int(_hexlify(_urandom(16)), 16)
+ a = int.from_bytes(_urandom(32), 'big')
except NotImplementedError:
import time
a = int(time.time() * 256) # use fractional seconds
+ if version == 2:
+ if isinstance(a, (str, bytes, bytearray)):
+ if isinstance(a, str):
+ a = a.encode("utf8")
+ a += _sha512(a).digest()
+ a = int.from_bytes(a, 'big')
+
super().seed(a)
self.gauss_next = None
@@ -127,10 +138,10 @@ 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(Random, self).setstate(internalstate)
+ super().setstate(internalstate)
else:
raise ValueError("state with version %s passed to "
"Random.setstate() of version %s" %
@@ -152,13 +163,13 @@ class Random(_random.Random):
## -------------------- integer methods -------------------
- def randrange(self, start, stop=None, step=1, int=int, default=None,
- maxwidth=1<<BPF):
+ def randrange(self, start, stop=None, step=1, int=int):
"""Choose a random item from range(start, stop[, step]).
This fixes the problem with randint() which includes the
endpoint; in Python this is usually not what you want.
- Do not supply the 'int', 'default', and 'maxwidth' arguments.
+
+ Do not supply the 'int' argument.
"""
# This code is a bit messy to make it fast for the
@@ -166,11 +177,9 @@ class Random(_random.Random):
istart = int(start)
if istart != start:
raise ValueError("non-integer arg 1 for randrange()")
- if stop is default:
+ if stop is None:
if istart > 0:
- if istart >= maxwidth:
- return self._randbelow(istart)
- return int(self.random() * istart)
+ return self._randbelow(istart)
raise ValueError("empty range for randrange()")
# stop argument supplied.
@@ -179,22 +188,7 @@ class Random(_random.Random):
raise ValueError("non-integer stop for randrange()")
width = istop - istart
if step == 1 and width > 0:
- # Note that
- # int(istart + self.random()*width)
- # instead would be incorrect. For example, consider istart
- # = -2 and istop = 0. Then the guts would be in
- # -2.0 to 0.0 exclusive on both ends (ignoring that random()
- # might return 0.0), and because int() truncates toward 0, the
- # final result would be -1 or 0 (instead of -2 or -1).
- # istart + int(self.random()*width)
- # would also be incorrect, for a subtler reason: the RHS
- # can return a long, and then randrange() would also return
- # a long, but we're supposed to return an int (for backward
- # compatibility).
-
- if width >= maxwidth:
- return int(istart + self._randbelow(width))
- return int(istart + int(self.random()*width))
+ return istart + self._randbelow(width)
if step == 1:
raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width))
@@ -212,9 +206,7 @@ class Random(_random.Random):
if n <= 0:
raise ValueError("empty range for randrange()")
- if n >= maxwidth:
- return istart + istep*self._randbelow(n)
- return istart + istep*int(self.random() * n)
+ return istart + istep*self._randbelow(n)
def randint(self, a, b):
"""Return random integer in range [a, b], including both end points.
@@ -222,38 +214,43 @@ class Random(_random.Random):
return self.randrange(a, b+1)
- def _randbelow(self, n, _log=_log, int=int, _maxwidth=1<<BPF,
- _Method=_MethodType, _BuiltinMethod=_BuiltinMethodType):
- """Return a random int in the range [0,n)
-
- Handles the case where n has more bits than returned
- by a single call to the underlying generator.
- """
-
- try:
- getrandbits = self.getrandbits
- except AttributeError:
- pass
- else:
- # Only call self.getrandbits if the original random() builtin method
- # has not been overridden or if a new getrandbits() was supplied.
- # This assures that the two methods correspond.
- if type(self.random) is _BuiltinMethod or type(getrandbits) is _Method:
- k = int(1.00001 + _log(n-1, 2.0)) # 2**k > n-1 > 2**(k-2)
+ def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,
+ Method=_MethodType, BuiltinMethod=_BuiltinMethodType):
+ "Return a random int in the range [0,n). Raises ValueError if n==0."
+
+ getrandbits = self.getrandbits
+ # Only call self.getrandbits if the original random() builtin method
+ # has not been overridden or if a new getrandbits() was supplied.
+ if type(self.random) is BuiltinMethod or type(getrandbits) is Method:
+ k = n.bit_length() # don't use (n-1) here because n can be 1
+ r = getrandbits(k) # 0 <= r < 2**k
+ while r >= n:
r = getrandbits(k)
- while r >= n:
- r = getrandbits(k)
- return r
- if n >= _maxwidth:
+ return r
+ # There's an overriden random() method but no new getrandbits() method,
+ # so we can only use random() from here.
+ random = self.random
+ if n >= maxsize:
_warn("Underlying random() generator does not supply \n"
- "enough bits to choose from a population range this large")
- return int(self.random() * n)
+ "enough bits to choose from a population range this large.\n"
+ "To remove the range limitation, add a getrandbits() method.")
+ return int(random() * n)
+ rem = maxsize % n
+ limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0
+ r = random()
+ while r >= limit:
+ r = random()
+ return int(r*maxsize) % n
## -------------------- sequence methods -------------------
def choice(self, seq):
"""Choose a random element from a non-empty sequence."""
- return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
+ try:
+ i = self._randbelow(len(seq))
+ except ValueError:
+ raise IndexError('Cannot choose from an empty sequence')
+ return seq[i]
def shuffle(self, x, random=None, int=int):
"""x, random=random.random -> shuffle list x in place; return None.
@@ -262,11 +259,10 @@ class Random(_random.Random):
float in [0.0, 1.0); by default, the standard random.random.
"""
- if random is None:
- random = self.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 = int(random() * (i+1))
+ j = randbelow(i+1) if random is None else int(random() * (i+1))
x[i], x[j] = x[j], x[i]
def sample(self, population, k):
@@ -301,11 +297,10 @@ class Random(_random.Random):
population = tuple(population)
if not isinstance(population, _collections.Sequence):
raise TypeError("Population must be a sequence or Set. For dicts, use list(d).")
- random = self.random
+ randbelow = self._randbelow
n = len(population)
if not 0 <= k <= n:
raise ValueError("Sample larger than population")
- _int = int
result = [None] * k
setsize = 21 # size of a small set minus size of an empty list
if k > 5:
@@ -314,16 +309,16 @@ class Random(_random.Random):
# An n-length list is smaller than a k-length set
pool = list(population)
for i in range(k): # invariant: non-selected at [0,n-i)
- j = _int(random() * (n-i))
+ j = randbelow(n-i)
result[i] = pool[j]
pool[j] = pool[n-i-1] # move non-selected item into vacancy
else:
selected = set()
selected_add = selected.add
for i in range(k):
- j = _int(random() * n)
+ j = randbelow(n)
while j in selected:
- j = _int(random() * n)
+ j = randbelow(n)
selected_add(j)
result[i] = population[j]
return result
@@ -613,7 +608,7 @@ class Random(_random.Random):
# Jain, pg. 495
u = 1.0 - self.random()
- return 1.0 / pow(u, 1.0/alpha)
+ return 1.0 / u ** (1.0/alpha)
## -------------------- Weibull --------------------
@@ -626,7 +621,7 @@ class Random(_random.Random):
# Jain, pg. 499; bug fix courtesy Bill Arms
u = 1.0 - self.random()
- return alpha * pow(-_log(u), 1.0/beta)
+ return alpha * (-_log(u)) ** (1.0/beta)
## --------------- Operating System Random Source ------------------
@@ -640,7 +635,7 @@ class SystemRandom(Random):
def random(self):
"""Get the next random number in the range [0.0, 1.0)."""
- return (int(_hexlify(_urandom(7)), 16) >> 3) * RECIP_BPF
+ return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF
def getrandbits(self, k):
"""getrandbits(k) -> x. Generates a long int with k random bits."""
@@ -648,9 +643,9 @@ class SystemRandom(Random):
raise ValueError('number of bits must be greater than zero')
if k != int(k):
raise TypeError('number of bits should be an integer')
- bytes = (k + 7) // 8 # bits / 8 and rounded up
- x = int(_hexlify(_urandom(bytes)), 16)
- return x >> (bytes * 8 - k) # trim excess bits
+ numbytes = (k + 7) // 8 # bits / 8 and rounded up
+ x = int.from_bytes(_urandom(numbytes), 'big')
+ return x >> (numbytes * 8 - k) # trim excess bits
def seed(self, *args, **kwds):
"Stub method. Not used for a system random number generator."