Cosmetic changes after some sleep; no change in semantics.
This commit is contained in:
parent
a0ac40c530
commit
cd80410854
196
Lib/random.py
196
Lib/random.py
|
@ -27,7 +27,10 @@ Translated from anonymously contributed C/C++ source.
|
||||||
|
|
||||||
Multi-threading note: the random number generator used here is not
|
Multi-threading note: the random number generator used here is not
|
||||||
thread-safe; it is possible that two calls return the same random
|
thread-safe; it is possible that two calls return the same random
|
||||||
value.
|
value. But you can instantiate a different instance of Random() in
|
||||||
|
each thread to get generators that don't share state, then use
|
||||||
|
.setstate() and .jumpahead() to move the generators to disjoint
|
||||||
|
segments of the full period.
|
||||||
"""
|
"""
|
||||||
# XXX The docstring sucks.
|
# XXX The docstring sucks.
|
||||||
|
|
||||||
|
@ -71,9 +74,11 @@ class Random:
|
||||||
self.seed(x)
|
self.seed(x)
|
||||||
self.gauss_next = None
|
self.gauss_next = None
|
||||||
|
|
||||||
|
## -------------------- core generator -------------------
|
||||||
|
|
||||||
# Specific to Wichmann-Hill generator. Subclasses wishing to use a
|
# Specific to Wichmann-Hill generator. Subclasses wishing to use a
|
||||||
# different core generator should override the seed(), random(),
|
# different core generator should override the seed(), random(),
|
||||||
# getstate(), setstate(), and jumpahead() methods.
|
# getstate(), setstate() and jumpahead() methods.
|
||||||
|
|
||||||
def __whseed(self, x=0, y=0, z=0):
|
def __whseed(self, x=0, y=0, z=0):
|
||||||
"""Set the Wichmann-Hill seed from (x, y, z).
|
"""Set the Wichmann-Hill seed from (x, y, z).
|
||||||
|
@ -96,66 +101,6 @@ class Random:
|
||||||
# Zero is a poor seed, so substitute 1
|
# Zero is a poor seed, so substitute 1
|
||||||
self._seed = (x or 1, y or 1, z or 1)
|
self._seed = (x or 1, y or 1, z or 1)
|
||||||
|
|
||||||
def seed(self, a=None):
|
|
||||||
"""Seed from hashable value
|
|
||||||
|
|
||||||
None or no argument seeds from current time.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if a is None:
|
|
||||||
self.__whseed()
|
|
||||||
return
|
|
||||||
a = hash(a)
|
|
||||||
a, x = divmod(a, 256)
|
|
||||||
a, y = divmod(a, 256)
|
|
||||||
a, z = divmod(a, 256)
|
|
||||||
x = (x + a) % 256 or 1
|
|
||||||
y = (y + a) % 256 or 1
|
|
||||||
z = (z + a) % 256 or 1
|
|
||||||
self.__whseed(x, y, z)
|
|
||||||
|
|
||||||
def getstate(self):
|
|
||||||
"""Return internal state; can be passed to setstate() later."""
|
|
||||||
return self.VERSION, self._seed, self.gauss_next
|
|
||||||
|
|
||||||
def __getstate__(self): # for pickle
|
|
||||||
return self.getstate()
|
|
||||||
|
|
||||||
def setstate(self, state):
|
|
||||||
"""Restore internal state from object returned by getstate()."""
|
|
||||||
version = state[0]
|
|
||||||
if version == 1:
|
|
||||||
version, self._seed, self.gauss_next = state
|
|
||||||
else:
|
|
||||||
raise ValueError("state with version %s passed to "
|
|
||||||
"Random.setstate() of version %s" %
|
|
||||||
(version, self.VERSION))
|
|
||||||
|
|
||||||
def __setstate__(self, state): # for pickle
|
|
||||||
self.setstate(state)
|
|
||||||
|
|
||||||
def jumpahead(self, n):
|
|
||||||
"""Act as if n calls to random() were made, but quickly.
|
|
||||||
|
|
||||||
n is an int, greater than or equal to 0.
|
|
||||||
|
|
||||||
Example use: If you have 2 threads and know that each will
|
|
||||||
consume no more than a million random numbers, create two Random
|
|
||||||
objects r1 and r2, then do
|
|
||||||
r2.setstate(r1.getstate())
|
|
||||||
r2.jumpahead(1000000)
|
|
||||||
Then r1 and r2 will use guaranteed-disjoint segments of the full
|
|
||||||
period.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not n >= 0:
|
|
||||||
raise ValueError("n must be >= 0")
|
|
||||||
x, y, z = self._seed
|
|
||||||
x = int(x * pow(171, n, 30269)) % 30269
|
|
||||||
y = int(y * pow(172, n, 30307)) % 30307
|
|
||||||
z = int(z * pow(170, n, 30323)) % 30323
|
|
||||||
self._seed = x, y, z
|
|
||||||
|
|
||||||
def random(self):
|
def random(self):
|
||||||
"""Get the next random number in the range [0.0, 1.0)."""
|
"""Get the next random number in the range [0.0, 1.0)."""
|
||||||
|
|
||||||
|
@ -187,6 +132,75 @@ class Random:
|
||||||
# never return 0.0 (asserted by Tim; proof too long for a comment).
|
# never return 0.0 (asserted by Tim; proof too long for a comment).
|
||||||
return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
|
return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
|
||||||
|
|
||||||
|
def seed(self, a=None):
|
||||||
|
"""Seed from hashable object's hash code.
|
||||||
|
|
||||||
|
None or no argument seeds from current time. It is not guaranteed
|
||||||
|
that objects with distinct hash codes lead to distinct internal
|
||||||
|
states.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if a is None:
|
||||||
|
self.__whseed()
|
||||||
|
return
|
||||||
|
a = hash(a)
|
||||||
|
a, x = divmod(a, 256)
|
||||||
|
a, y = divmod(a, 256)
|
||||||
|
a, z = divmod(a, 256)
|
||||||
|
x = (x + a) % 256 or 1
|
||||||
|
y = (y + a) % 256 or 1
|
||||||
|
z = (z + a) % 256 or 1
|
||||||
|
self.__whseed(x, y, z)
|
||||||
|
|
||||||
|
def getstate(self):
|
||||||
|
"""Return internal state; can be passed to setstate() later."""
|
||||||
|
return self.VERSION, self._seed, self.gauss_next
|
||||||
|
|
||||||
|
def setstate(self, state):
|
||||||
|
"""Restore internal state from object returned by getstate()."""
|
||||||
|
version = state[0]
|
||||||
|
if version == 1:
|
||||||
|
version, self._seed, self.gauss_next = state
|
||||||
|
else:
|
||||||
|
raise ValueError("state with version %s passed to "
|
||||||
|
"Random.setstate() of version %s" %
|
||||||
|
(version, self.VERSION))
|
||||||
|
|
||||||
|
def jumpahead(self, n):
|
||||||
|
"""Act as if n calls to random() were made, but quickly.
|
||||||
|
|
||||||
|
n is an int, greater than or equal to 0.
|
||||||
|
|
||||||
|
Example use: If you have 2 threads and know that each will
|
||||||
|
consume no more than a million random numbers, create two Random
|
||||||
|
objects r1 and r2, then do
|
||||||
|
r2.setstate(r1.getstate())
|
||||||
|
r2.jumpahead(1000000)
|
||||||
|
Then r1 and r2 will use guaranteed-disjoint segments of the full
|
||||||
|
period.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not n >= 0:
|
||||||
|
raise ValueError("n must be >= 0")
|
||||||
|
x, y, z = self._seed
|
||||||
|
x = int(x * pow(171, n, 30269)) % 30269
|
||||||
|
y = int(y * pow(172, n, 30307)) % 30307
|
||||||
|
z = int(z * pow(170, n, 30323)) % 30323
|
||||||
|
self._seed = x, y, z
|
||||||
|
|
||||||
|
## ---- Methods below this point do not need to be overridden when
|
||||||
|
## ---- subclassing for the purpose of using a different core generator.
|
||||||
|
|
||||||
|
## -------------------- pickle support -------------------
|
||||||
|
|
||||||
|
def __getstate__(self): # for pickle
|
||||||
|
return self.getstate()
|
||||||
|
|
||||||
|
def __setstate__(self, state): # for pickle
|
||||||
|
self.setstate(state)
|
||||||
|
|
||||||
|
## -------------------- integer methods -------------------
|
||||||
|
|
||||||
def randrange(self, start, stop=None, step=1, int=int, default=None):
|
def randrange(self, start, stop=None, step=1, int=int, default=None):
|
||||||
"""Choose a random item from range(start, stop[, step]).
|
"""Choose a random item from range(start, stop[, step]).
|
||||||
|
|
||||||
|
@ -227,14 +241,15 @@ class Random:
|
||||||
return istart + istep*int(self.random() * n)
|
return istart + istep*int(self.random() * n)
|
||||||
|
|
||||||
def randint(self, a, b):
|
def randint(self, a, b):
|
||||||
"""Get a random integer in the range [a, b] including
|
"""Return random integer in range [a, b], including both end points.
|
||||||
both end points.
|
|
||||||
|
|
||||||
(Deprecated; use randrange below.)
|
(Deprecated; use randrange(a, b+1).)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.randrange(a, b+1)
|
return self.randrange(a, b+1)
|
||||||
|
|
||||||
|
## -------------------- sequence methods -------------------
|
||||||
|
|
||||||
def choice(self, seq):
|
def choice(self, seq):
|
||||||
"""Choose a random element from a non-empty sequence."""
|
"""Choose a random element from a non-empty sequence."""
|
||||||
return seq[int(self.random() * len(seq))]
|
return seq[int(self.random() * len(seq))]
|
||||||
|
@ -254,17 +269,19 @@ class Random:
|
||||||
if random is None:
|
if random is None:
|
||||||
random = self.random
|
random = self.random
|
||||||
for i in xrange(len(x)-1, 0, -1):
|
for i in xrange(len(x)-1, 0, -1):
|
||||||
# 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 = int(random() * (i+1))
|
||||||
x[i], x[j] = x[j], x[i]
|
x[i], x[j] = x[j], x[i]
|
||||||
|
|
||||||
# -------------------- uniform distribution -------------------
|
## -------------------- real-valued distributions -------------------
|
||||||
|
|
||||||
|
## -------------------- uniform distribution -------------------
|
||||||
|
|
||||||
def uniform(self, a, b):
|
def uniform(self, a, b):
|
||||||
"""Get a random number in the range [a, b)."""
|
"""Get a random number in the range [a, b)."""
|
||||||
return a + (b-a) * self.random()
|
return a + (b-a) * self.random()
|
||||||
|
|
||||||
# -------------------- normal distribution --------------------
|
## -------------------- normal distribution --------------------
|
||||||
|
|
||||||
def normalvariate(self, mu, sigma):
|
def normalvariate(self, mu, sigma):
|
||||||
# mu = mean, sigma = standard deviation
|
# mu = mean, sigma = standard deviation
|
||||||
|
@ -284,12 +301,12 @@ class Random:
|
||||||
break
|
break
|
||||||
return mu + z*sigma
|
return mu + z*sigma
|
||||||
|
|
||||||
# -------------------- lognormal distribution --------------------
|
## -------------------- lognormal distribution --------------------
|
||||||
|
|
||||||
def lognormvariate(self, mu, sigma):
|
def lognormvariate(self, mu, sigma):
|
||||||
return _exp(self.normalvariate(mu, sigma))
|
return _exp(self.normalvariate(mu, sigma))
|
||||||
|
|
||||||
# -------------------- circular uniform --------------------
|
## -------------------- circular uniform --------------------
|
||||||
|
|
||||||
def cunifvariate(self, mean, arc):
|
def cunifvariate(self, mean, arc):
|
||||||
# mean: mean angle (in radians between 0 and pi)
|
# mean: mean angle (in radians between 0 and pi)
|
||||||
|
@ -297,7 +314,7 @@ class Random:
|
||||||
|
|
||||||
return (mean + arc * (self.random() - 0.5)) % _pi
|
return (mean + arc * (self.random() - 0.5)) % _pi
|
||||||
|
|
||||||
# -------------------- exponential distribution --------------------
|
## -------------------- exponential distribution --------------------
|
||||||
|
|
||||||
def expovariate(self, lambd):
|
def expovariate(self, lambd):
|
||||||
# lambd: rate lambd = 1/mean
|
# lambd: rate lambd = 1/mean
|
||||||
|
@ -309,7 +326,7 @@ class Random:
|
||||||
u = random()
|
u = random()
|
||||||
return -_log(u)/lambd
|
return -_log(u)/lambd
|
||||||
|
|
||||||
# -------------------- von Mises distribution --------------------
|
## -------------------- von Mises distribution --------------------
|
||||||
|
|
||||||
def vonmisesvariate(self, mu, kappa):
|
def vonmisesvariate(self, mu, kappa):
|
||||||
# mu: mean angle (in radians between 0 and 2*pi)
|
# mu: mean angle (in radians between 0 and 2*pi)
|
||||||
|
@ -351,7 +368,7 @@ class Random:
|
||||||
|
|
||||||
return theta
|
return theta
|
||||||
|
|
||||||
# -------------------- gamma distribution --------------------
|
## -------------------- gamma distribution --------------------
|
||||||
|
|
||||||
def gammavariate(self, alpha, beta):
|
def gammavariate(self, alpha, beta):
|
||||||
# beta times standard gamma
|
# beta times standard gamma
|
||||||
|
@ -410,7 +427,7 @@ class Random:
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
# -------------------- Gauss (faster alternative) --------------------
|
## -------------------- Gauss (faster alternative) --------------------
|
||||||
|
|
||||||
def gauss(self, mu, sigma):
|
def gauss(self, mu, sigma):
|
||||||
|
|
||||||
|
@ -443,7 +460,7 @@ class Random:
|
||||||
|
|
||||||
return mu + z*sigma
|
return mu + z*sigma
|
||||||
|
|
||||||
# -------------------- beta --------------------
|
## -------------------- beta --------------------
|
||||||
|
|
||||||
def betavariate(self, alpha, beta):
|
def betavariate(self, alpha, beta):
|
||||||
|
|
||||||
|
@ -453,7 +470,7 @@ class Random:
|
||||||
z = self.expovariate(1.0/beta)
|
z = self.expovariate(1.0/beta)
|
||||||
return z/(y+z)
|
return z/(y+z)
|
||||||
|
|
||||||
# -------------------- Pareto --------------------
|
## -------------------- Pareto --------------------
|
||||||
|
|
||||||
def paretovariate(self, alpha):
|
def paretovariate(self, alpha):
|
||||||
# Jain, pg. 495
|
# Jain, pg. 495
|
||||||
|
@ -461,7 +478,7 @@ class Random:
|
||||||
u = self.random()
|
u = self.random()
|
||||||
return 1.0 / pow(u, 1.0/alpha)
|
return 1.0 / pow(u, 1.0/alpha)
|
||||||
|
|
||||||
# -------------------- Weibull --------------------
|
## -------------------- Weibull --------------------
|
||||||
|
|
||||||
def weibullvariate(self, alpha, beta):
|
def weibullvariate(self, alpha, beta):
|
||||||
# Jain, pg. 499; bug fix courtesy Bill Arms
|
# Jain, pg. 499; bug fix courtesy Bill Arms
|
||||||
|
@ -469,7 +486,7 @@ class Random:
|
||||||
u = self.random()
|
u = self.random()
|
||||||
return alpha * pow(-_log(u), 1.0/beta)
|
return alpha * pow(-_log(u), 1.0/beta)
|
||||||
|
|
||||||
# -------------------- test program --------------------
|
## -------------------- test program --------------------
|
||||||
|
|
||||||
def _test_generator(n, funccall):
|
def _test_generator(n, funccall):
|
||||||
import time
|
import time
|
||||||
|
@ -493,17 +510,6 @@ def _test_generator(n, funccall):
|
||||||
print 'avg %g, stddev %g, min %g, max %g' % \
|
print 'avg %g, stddev %g, min %g, max %g' % \
|
||||||
(avg, stddev, smallest, largest)
|
(avg, stddev, smallest, largest)
|
||||||
|
|
||||||
s = getstate()
|
|
||||||
N = 1019
|
|
||||||
jumpahead(N)
|
|
||||||
r1 = random()
|
|
||||||
setstate(s)
|
|
||||||
for i in range(N): # now do it the slow way
|
|
||||||
random()
|
|
||||||
r2 = random()
|
|
||||||
if r1 != r2:
|
|
||||||
raise ValueError("jumpahead test failed " + `(N, r1, r2)`)
|
|
||||||
|
|
||||||
def _test(N=200):
|
def _test(N=200):
|
||||||
print 'TWOPI =', TWOPI
|
print 'TWOPI =', TWOPI
|
||||||
print 'LOG4 =', LOG4
|
print 'LOG4 =', LOG4
|
||||||
|
@ -526,6 +532,18 @@ def _test(N=200):
|
||||||
_test_generator(N, 'paretovariate(1.0)')
|
_test_generator(N, 'paretovariate(1.0)')
|
||||||
_test_generator(N, 'weibullvariate(1.0, 1.0)')
|
_test_generator(N, 'weibullvariate(1.0, 1.0)')
|
||||||
|
|
||||||
|
# Test jumpahead.
|
||||||
|
s = getstate()
|
||||||
|
jumpahead(N)
|
||||||
|
r1 = random()
|
||||||
|
# now do it the slow way
|
||||||
|
setstate(s)
|
||||||
|
for i in range(N):
|
||||||
|
random()
|
||||||
|
r2 = random()
|
||||||
|
if r1 != r2:
|
||||||
|
raise ValueError("jumpahead test failed " + `(N, r1, r2)`)
|
||||||
|
|
||||||
# Initialize from current time.
|
# Initialize from current time.
|
||||||
_inst = Random()
|
_inst = Random()
|
||||||
seed = _inst.seed
|
seed = _inst.seed
|
||||||
|
|
Loading…
Reference in New Issue