summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-01-25 20:25:57 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-01-25 20:25:57 (GMT)
commitcd804108548e1939bd8646634ed52ef388ee9f44 (patch)
tree71c2554a7b202f10e6eb14079f09c9676e1092d1
parenta0ac40c530aea691ebeee3cb3935e6ab99cd1353 (diff)
downloadcpython-cd804108548e1939bd8646634ed52ef388ee9f44.zip
cpython-cd804108548e1939bd8646634ed52ef388ee9f44.tar.gz
cpython-cd804108548e1939bd8646634ed52ef388ee9f44.tar.bz2
Cosmetic changes after some sleep; no change in semantics.
-rw-r--r--Lib/random.py146
1 files changed, 82 insertions, 64 deletions
diff --git a/Lib/random.py b/Lib/random.py
index 1b91886..d098d39 100644
--- a/Lib/random.py
+++ b/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
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.
@@ -71,9 +74,11 @@ class Random:
self.seed(x)
self.gauss_next = None
+## -------------------- core generator -------------------
+
# Specific to Wichmann-Hill generator. Subclasses wishing to use a
# 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):
"""Set the Wichmann-Hill seed from (x, y, z).
@@ -96,10 +101,43 @@ class Random:
# Zero is a poor seed, so substitute 1
self._seed = (x or 1, y or 1, z or 1)
+ def random(self):
+ """Get the next random number in the range [0.0, 1.0)."""
+
+ # Wichman-Hill random number generator.
+ #
+ # Wichmann, B. A. & Hill, I. D. (1982)
+ # Algorithm AS 183:
+ # An efficient and portable pseudo-random number generator
+ # Applied Statistics 31 (1982) 188-190
+ #
+ # see also:
+ # Correction to Algorithm AS 183
+ # Applied Statistics 33 (1984) 123
+ #
+ # McLeod, A. I. (1985)
+ # A remark on Algorithm AS 183
+ # Applied Statistics 34 (1985),198-200
+
+ # This part is thread-unsafe:
+ # BEGIN CRITICAL SECTION
+ x, y, z = self._seed
+ x = (171 * x) % 30269
+ y = (172 * y) % 30307
+ z = (170 * z) % 30323
+ self._seed = x, y, z
+ # END CRITICAL SECTION
+
+ # Note: on a platform using IEEE-754 double arithmetic, this can
+ # 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
+
def seed(self, a=None):
- """Seed from hashable value
+ """Seed from hashable object's hash code.
- None or no argument seeds from current time.
+ 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:
@@ -118,9 +156,6 @@ class Random:
"""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]
@@ -131,9 +166,6 @@ class Random:
"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.
@@ -156,36 +188,18 @@ class Random:
z = int(z * pow(170, n, 30323)) % 30323
self._seed = x, y, z
- def random(self):
- """Get the next random number in the range [0.0, 1.0)."""
+## ---- Methods below this point do not need to be overridden when
+## ---- subclassing for the purpose of using a different core generator.
- # Wichman-Hill random number generator.
- #
- # Wichmann, B. A. & Hill, I. D. (1982)
- # Algorithm AS 183:
- # An efficient and portable pseudo-random number generator
- # Applied Statistics 31 (1982) 188-190
- #
- # see also:
- # Correction to Algorithm AS 183
- # Applied Statistics 33 (1984) 123
- #
- # McLeod, A. I. (1985)
- # A remark on Algorithm AS 183
- # Applied Statistics 34 (1985),198-200
+## -------------------- pickle support -------------------
- # This part is thread-unsafe:
- # BEGIN CRITICAL SECTION
- x, y, z = self._seed
- x = (171 * x) % 30269
- y = (172 * y) % 30307
- z = (170 * z) % 30323
- self._seed = x, y, z
- # END CRITICAL SECTION
+ def __getstate__(self): # for pickle
+ return self.getstate()
- # Note: on a platform using IEEE-754 double arithmetic, this can
- # 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
+ def __setstate__(self, state): # for pickle
+ self.setstate(state)
+
+## -------------------- integer methods -------------------
def randrange(self, start, stop=None, step=1, int=int, default=None):
"""Choose a random item from range(start, stop[, step]).
@@ -227,14 +241,15 @@ class Random:
return istart + istep*int(self.random() * n)
def randint(self, a, b):
- """Get a random integer in the range [a, b] including
- both end points.
+ """Return random integer in range [a, b], including both end points.
- (Deprecated; use randrange below.)
+ (Deprecated; use randrange(a, b+1).)
"""
return self.randrange(a, b+1)
+## -------------------- sequence methods -------------------
+
def choice(self, seq):
"""Choose a random element from a non-empty sequence."""
return seq[int(self.random() * len(seq))]
@@ -254,17 +269,19 @@ class Random:
if random is None:
random = self.random
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))
x[i], x[j] = x[j], x[i]
-# -------------------- uniform distribution -------------------
+## -------------------- real-valued distributions -------------------
+
+## -------------------- uniform distribution -------------------
def uniform(self, a, b):
"""Get a random number in the range [a, b)."""
return a + (b-a) * self.random()
-# -------------------- normal distribution --------------------
+## -------------------- normal distribution --------------------
def normalvariate(self, mu, sigma):
# mu = mean, sigma = standard deviation
@@ -284,12 +301,12 @@ class Random:
break
return mu + z*sigma
-# -------------------- lognormal distribution --------------------
+## -------------------- lognormal distribution --------------------
def lognormvariate(self, mu, sigma):
return _exp(self.normalvariate(mu, sigma))
-# -------------------- circular uniform --------------------
+## -------------------- circular uniform --------------------
def cunifvariate(self, mean, arc):
# mean: mean angle (in radians between 0 and pi)
@@ -297,7 +314,7 @@ class Random:
return (mean + arc * (self.random() - 0.5)) % _pi
-# -------------------- exponential distribution --------------------
+## -------------------- exponential distribution --------------------
def expovariate(self, lambd):
# lambd: rate lambd = 1/mean
@@ -309,7 +326,7 @@ class Random:
u = random()
return -_log(u)/lambd
-# -------------------- von Mises distribution --------------------
+## -------------------- von Mises distribution --------------------
def vonmisesvariate(self, mu, kappa):
# mu: mean angle (in radians between 0 and 2*pi)
@@ -351,7 +368,7 @@ class Random:
return theta
-# -------------------- gamma distribution --------------------
+## -------------------- gamma distribution --------------------
def gammavariate(self, alpha, beta):
# beta times standard gamma
@@ -410,7 +427,7 @@ class Random:
return x
-# -------------------- Gauss (faster alternative) --------------------
+## -------------------- Gauss (faster alternative) --------------------
def gauss(self, mu, sigma):
@@ -443,7 +460,7 @@ class Random:
return mu + z*sigma
-# -------------------- beta --------------------
+## -------------------- beta --------------------
def betavariate(self, alpha, beta):
@@ -453,7 +470,7 @@ class Random:
z = self.expovariate(1.0/beta)
return z/(y+z)
-# -------------------- Pareto --------------------
+## -------------------- Pareto --------------------
def paretovariate(self, alpha):
# Jain, pg. 495
@@ -461,7 +478,7 @@ class Random:
u = self.random()
return 1.0 / pow(u, 1.0/alpha)
-# -------------------- Weibull --------------------
+## -------------------- Weibull --------------------
def weibullvariate(self, alpha, beta):
# Jain, pg. 499; bug fix courtesy Bill Arms
@@ -469,7 +486,7 @@ class Random:
u = self.random()
return alpha * pow(-_log(u), 1.0/beta)
-# -------------------- test program --------------------
+## -------------------- test program --------------------
def _test_generator(n, funccall):
import time
@@ -493,17 +510,6 @@ def _test_generator(n, funccall):
print 'avg %g, stddev %g, min %g, max %g' % \
(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):
print 'TWOPI =', TWOPI
print 'LOG4 =', LOG4
@@ -526,6 +532,18 @@ def _test(N=200):
_test_generator(N, 'paretovariate(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.
_inst = Random()
seed = _inst.seed