diff options
author | Tim Peters <tim.peters@gmail.com> | 2001-01-25 03:36:26 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2001-01-25 03:36:26 (GMT) |
commit | d7b5e88e8e40b77813ceb25dc28b87d672538403 (patch) | |
tree | ea8855e8c3d37f837eb3eeb505a6503083d19db1 | |
parent | 83125775e0a5c5088da0cb62b43e7cfd8a04fdc6 (diff) | |
download | cpython-d7b5e88e8e40b77813ceb25dc28b87d672538403.zip cpython-d7b5e88e8e40b77813ceb25dc28b87d672538403.tar.gz cpython-d7b5e88e8e40b77813ceb25dc28b87d672538403.tar.bz2 |
Reworked random.py so that it no longer depends on, and offers all the
functionality of, whrandom.py. Also closes all the "XXX" todos in
random.py. New frequently-requested functions/methods getstate() and
setstate(). All exported functions are now bound methods of a hidden
instance. Killed all unintended exports. Updated the docs.
FRED: The more I fiddle the docs, the less I understand the exact
intended use of the \var, \code, \method tags. Please review critically.
GUIDO: See email. I updated NEWS as if whrandom were deprecated; I
think it should be.
-rw-r--r-- | Doc/lib/librandom.tex | 86 | ||||
-rw-r--r-- | Lib/random.py | 670 | ||||
-rw-r--r-- | Misc/NEWS | 13 |
3 files changed, 481 insertions, 288 deletions
diff --git a/Doc/lib/librandom.tex b/Doc/lib/librandom.tex index 82a55e6..4e4d615 100644 --- a/Doc/lib/librandom.tex +++ b/Doc/lib/librandom.tex @@ -32,6 +32,18 @@ functions from multiple threads, you should explicitly serialize the calls. Else, because no critical sections are implemented internally, calls from different threads may see the same return values. +The functions supplied by this module are actually bound methods of a +hidden instance of the \var{random.Random} class. You can instantiate +your own instances of \var{Random} to get generators that don't share state. +This may be especially useful for multi-threaded programs, although there's +no simple way to seed the distinct generators to ensure that the generated +sequences won't overlap. Class \var{Random} can also be subclassed if you +want to use a different basic generator of your own devising: in that +case, override the \method{random()}, \method{seed()}, \method{getstate()} +and \method{setstate()} methods. + + +Bookkeeping functions: \begin{funcdesc}{seed}{\optional{x}} Initialize the basic random number generator. @@ -45,15 +57,19 @@ from different threads may see the same return values. the module is first imported. \end{funcdesc} -\begin{funcdesc}{choice}{seq} - Return a random element from the non-empty sequence \var{seq}. -\end{funcdesc} +\begin{funcdesc}{getstate}{} + Return an object capturing the current internal state of the generator. + This object can be passed to \code{setstate()} to restore the state. + \end{funcdesc} + +\begin{funcdesc}{setstate}{state} + \var{state} should have been obtained from a previous call to + \code{getstate()}, and \code{setstate()} restores the internal state + of the generate to what it was at the time \code{setstate()} was called. + \end{funcdesc} -\begin{funcdesc}{randint}{a, b} - \deprecated{2.0}{Use \function{randrange()} instead.} - Return a random integer \var{N} such that - \code{\var{a} <= \var{N} <= \var{b}}. -\end{funcdesc} + +Functions for integers: \begin{funcdesc}{randrange}{\optional{start,} stop\optional{, step}} Return a randomly selected element from \code{range(\var{start}, @@ -63,6 +79,37 @@ from different threads may see the same return values. \versionadded{1.5.2} \end{funcdesc} +\begin{funcdesc}{randint}{a, b} + \deprecated{2.0}{Use \function{randrange()} instead.} + Return a random integer \var{N} such that + \code{\var{a} <= \var{N} <= \var{b}}. +\end{funcdesc} + + +Functions for sequences: + +\begin{funcdesc}{choice}{seq} + Return a random element from the non-empty sequence \var{seq}. +\end{funcdesc} + +\begin{funcdesc}{shuffle}{x\optional{, random}} + Shuffle the sequence \var{x} in place. + The optional argument \var{random} is a 0-argument function + returning a random float in [0.0, 1.0); by default, this is the + function \function{random()}. + + Note that for even rather small \code{len(\var{x})}, the total + number of permutations of \var{x} is larger than the period of most + random number generators; this implies that most permutations of a + long sequence can never be generated. +\end{funcdesc} + + +The following functions generate specific real-valued distributions. +Function parameters are named after the corresponding variables in the +distribution's equation, as used in common mathematical practice; most of +these equations can be found in any statistics text. + \begin{funcdesc}{random}{} Return the next random floating point number in the range [0.0, 1.0). \end{funcdesc} @@ -72,14 +119,6 @@ from different threads may see the same return values. \code{\var{a} <= \var{N} < \var{b}}. \end{funcdesc} - -The following functions are defined to support specific distributions, -and all return real values. Function parameters are named after the -corresponding variables in the distribution's equation, as used in -common mathematical practice; most of these equations can be found in -any statistics text. - - \begin{funcdesc}{betavariate}{alpha, beta} Beta distribution. Conditions on the parameters are \code{\var{alpha} > -1} and \code{\var{beta} > -1}. @@ -143,21 +182,6 @@ any statistics text. \end{funcdesc} -This function does not represent a specific distribution, but -implements a standard useful algorithm: - -\begin{funcdesc}{shuffle}{x\optional{, random}} - Shuffle the sequence \var{x} in place. - The optional argument \var{random} is a 0-argument function - returning a random float in [0.0, 1.0); by default, this is the - function \function{random()}. - - Note that for even rather small \code{len(\var{x})}, the total - number of permutations of \var{x} is larger than the period of most - random number generators; this implies that most permutations of a - long sequence can never be generated. -\end{funcdesc} - \begin{seealso} \seetext{Wichmann, B. A. \& Hill, I. D., ``Algorithm AS 183: An efficient and portable pseudo-random number generator'', diff --git a/Lib/random.py b/Lib/random.py index d10ce78..a818f73 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -1,7 +1,17 @@ """Random variable generators. + integers + -------- + uniform within range + + sequences + --------- + pick random element + generate random permutation + distributions on the real line: ------------------------------ + uniform normal (Gaussian) lognormal negative exponential @@ -17,328 +27,429 @@ 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. See whrandom.py for more info. +value. """ +# XXX The docstring sucks. -import whrandom -from whrandom import random, uniform, randint, choice, randrange # For export! -from math import log, exp, pi, e, sqrt, acos, cos, sin +from math import log as _log, exp as _exp, pi as _pi, e as _e +from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin + +def _verify(name, expected): + computed = eval(name) + if abs(computed - expected) > 1e-7: + raise ValueError( + "computed value for %s deviates too much " + "(computed %g, expected %g)" % (name, computed, expected)) -# Interfaces to replace remaining needs for importing whrandom -# XXX TO DO: make the distribution functions below into methods. +NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) +_verify('NV_MAGICCONST', 1.71552776992141) -def makeseed(a=None): - """Turn a hashable value into three seed values for whrandom.seed(). +TWOPI = 2.0*_pi +_verify('TWOPI', 6.28318530718) - None or no argument returns (0, 0, 0), to seed from current time. +LOG4 = _log(4.0) +_verify('LOG4', 1.38629436111989) - """ - if a is None: - return (0, 0, 0) - 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 - return (x, y, z) +SG_MAGICCONST = 1.0 + _log(4.5) +_verify('SG_MAGICCONST', 2.50407739677627) -def seed(a=None): - """Seed the default generator from any hashable value. +del _verify - None or no argument seeds from current time. +# Translated by Guido van Rossum from C source provided by +# Adrian Baddeley. - """ - x, y, z = makeseed(a) - whrandom.seed(x, y, z) +class Random: -class generator(whrandom.whrandom): - """Random generator class.""" + VERSION = 1 # used by getstate/setstate - def __init__(self, a=None): - """Constructor. Seed from current time or hashable value.""" - self.seed(a) + def __init__(self, x=None): + """Initialize an instance. - def seed(self, a=None): - """Seed the generator from current time or hashable value.""" - x, y, z = makeseed(a) - whrandom.whrandom.seed(self, x, y, z) + Optional argument x controls seeding, as for Random.seed(). + """ -def new_generator(a=None): - """Return a new random generator instance.""" - return generator(a) + self.seed(x) + self.gauss_next = None -# Housekeeping function to verify that magic constants have been -# computed correctly + # Specific to Wichmann-Hill generator. Subclasses wishing to use a + # different core generator should override seed(), random(), getstate() + # and setstate(). -def verify(name, expected): - computed = eval(name) - if abs(computed - expected) > 1e-7: - raise ValueError, \ -'computed value for %s deviates too much (computed %g, expected %g)' % \ -(name, computed, expected) + def __whseed(self, x=0, y=0, z=0): + """Set the Wichmann-Hill seed from (x, y, z). + + These must be integers in the range [0, 256). + """ + + if not type(x) == type(y) == type(z) == type(0): + raise TypeError('seeds must be integers') + if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256): + raise ValueError('seeds must be in range(0, 256)') + if 0 == x == y == z: + # Initialize from current time + import time + t = long(time.time()) * 256 + t = int((t&0xffffff) ^ (t>>24)) + t, x = divmod(t, 256) + t, y = divmod(t, 256) + t, z = divmod(t, 256) + # Zero is a poor seed, so substitute 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() + 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 + 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 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 randrange(self, start, stop=None, step=1, int=int, default=None): + """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' and 'default' arguments. + """ + + # This code is a bit messy to make it fast for the + # common case while still doing adequate error checking + istart = int(start) + if istart != start: + raise ValueError, "non-integer arg 1 for randrange()" + if stop is default: + if istart > 0: + return int(self.random() * istart) + raise ValueError, "empty range for randrange()" + istop = int(stop) + if istop != stop: + raise ValueError, "non-integer stop for randrange()" + if step == 1: + if istart < istop: + return istart + int(self.random() * + (istop - istart)) + raise ValueError, "empty range for randrange()" + istep = int(step) + if istep != step: + raise ValueError, "non-integer step for randrange()" + if istep > 0: + n = (istop - istart + istep - 1) / istep + elif istep < 0: + n = (istop - istart + istep + 1) / istep + else: + raise ValueError, "zero step for randrange()" + + if n <= 0: + raise ValueError, "empty range for randrange()" + 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. + + (Deprecated; use randrange below.) + """ + + return self.randrange(a, b+1) + + def choice(self, seq): + """Choose a random element from a non-empty sequence.""" + return seq[int(self.random() * len(seq))] + + def shuffle(self, x, random=None, int=int): + """x, random=random.random -> shuffle list x in place; return None. + + Optional arg random is a 0-argument function returning a random + float in [0.0, 1.0); by default, the standard random.random. + + Note that for even rather small len(x), the total number of + permutations of x is larger than the period of most random number + generators; this implies that "most" permutations of a long + sequence can never be generated. + """ + + 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] + j = int(random() * (i+1)) + x[i], x[j] = x[j], x[i] + +# -------------------- uniform distribution ------------------- + + def uniform(self, a, b): + """Get a random number in the range [a, b).""" + return a + (b-a) * self.random() # -------------------- normal distribution -------------------- -NV_MAGICCONST = 4*exp(-0.5)/sqrt(2.0) -verify('NV_MAGICCONST', 1.71552776992141) -def normalvariate(mu, sigma): - # mu = mean, sigma = standard deviation - - # Uses Kinderman and Monahan method. Reference: Kinderman, - # A.J. and Monahan, J.F., "Computer generation of random - # variables using the ratio of uniform deviates", ACM Trans - # Math Software, 3, (1977), pp257-260. - - while 1: - u1 = random() - u2 = random() - z = NV_MAGICCONST*(u1-0.5)/u2 - zz = z*z/4.0 - if zz <= -log(u2): - break - return mu+z*sigma + def normalvariate(self, mu, sigma): + # mu = mean, sigma = standard deviation + + # Uses Kinderman and Monahan method. Reference: Kinderman, + # A.J. and Monahan, J.F., "Computer generation of random + # variables using the ratio of uniform deviates", ACM Trans + # Math Software, 3, (1977), pp257-260. + + random = self.random + while 1: + u1 = random() + u2 = random() + z = NV_MAGICCONST*(u1-0.5)/u2 + zz = z*z/4.0 + if zz <= -_log(u2): + break + return mu + z*sigma # -------------------- lognormal distribution -------------------- -def lognormvariate(mu, sigma): - return exp(normalvariate(mu, sigma)) + def lognormvariate(self, mu, sigma): + return _exp(self.normalvariate(mu, sigma)) # -------------------- circular uniform -------------------- -def cunifvariate(mean, arc): - # mean: mean angle (in radians between 0 and pi) - # arc: range of distribution (in radians between 0 and pi) + def cunifvariate(self, mean, arc): + # mean: mean angle (in radians between 0 and pi) + # arc: range of distribution (in radians between 0 and pi) - return (mean + arc * (random() - 0.5)) % pi + return (mean + arc * (self.random() - 0.5)) % _pi # -------------------- exponential distribution -------------------- -def expovariate(lambd): - # lambd: rate lambd = 1/mean - # ('lambda' is a Python reserved word) + def expovariate(self, lambd): + # lambd: rate lambd = 1/mean + # ('lambda' is a Python reserved word) - u = random() - while u <= 1e-7: + random = self.random u = random() - return -log(u)/lambd + while u <= 1e-7: + u = random() + return -_log(u)/lambd # -------------------- von Mises distribution -------------------- -TWOPI = 2.0*pi -verify('TWOPI', 6.28318530718) - -def vonmisesvariate(mu, kappa): - # mu: mean angle (in radians between 0 and 2*pi) - # kappa: concentration parameter kappa (>= 0) - # if kappa = 0 generate uniform random angle + def vonmisesvariate(self, mu, kappa): + # mu: mean angle (in radians between 0 and 2*pi) + # kappa: concentration parameter kappa (>= 0) + # if kappa = 0 generate uniform random angle - # Based upon an algorithm published in: Fisher, N.I., - # "Statistical Analysis of Circular Data", Cambridge - # University Press, 1993. + # Based upon an algorithm published in: Fisher, N.I., + # "Statistical Analysis of Circular Data", Cambridge + # University Press, 1993. - # Thanks to Magnus Kessler for a correction to the - # implementation of step 4. + # Thanks to Magnus Kessler for a correction to the + # implementation of step 4. - if kappa <= 1e-6: - return TWOPI * random() + random = self.random + if kappa <= 1e-6: + return TWOPI * random() - a = 1.0 + sqrt(1.0 + 4.0 * kappa * kappa) - b = (a - sqrt(2.0 * a))/(2.0 * kappa) - r = (1.0 + b * b)/(2.0 * b) + a = 1.0 + _sqrt(1.0 + 4.0 * kappa * kappa) + b = (a - _sqrt(2.0 * a))/(2.0 * kappa) + r = (1.0 + b * b)/(2.0 * b) - while 1: - u1 = random() + while 1: + u1 = random() - z = cos(pi * u1) - f = (1.0 + r * z)/(r + z) - c = kappa * (r - f) + z = _cos(_pi * u1) + f = (1.0 + r * z)/(r + z) + c = kappa * (r - f) - u2 = random() + u2 = random() - if not (u2 >= c * (2.0 - c) and u2 > c * exp(1.0 - c)): - break + if not (u2 >= c * (2.0 - c) and u2 > c * _exp(1.0 - c)): + break - u3 = random() - if u3 > 0.5: - theta = (mu % TWOPI) + acos(f) - else: - theta = (mu % TWOPI) - acos(f) + u3 = random() + if u3 > 0.5: + theta = (mu % TWOPI) + _acos(f) + else: + theta = (mu % TWOPI) - _acos(f) - return theta + return theta # -------------------- gamma distribution -------------------- -LOG4 = log(4.0) -verify('LOG4', 1.38629436111989) - -def gammavariate(alpha, beta): - # beta times standard gamma - ainv = sqrt(2.0 * alpha - 1.0) - return beta * stdgamma(alpha, ainv, alpha - LOG4, alpha + ainv) - -SG_MAGICCONST = 1.0 + log(4.5) -verify('SG_MAGICCONST', 2.50407739677627) - -def stdgamma(alpha, ainv, bbb, ccc): - # ainv = sqrt(2 * alpha - 1) - # bbb = alpha - log(4) - # ccc = alpha + ainv - - if alpha <= 0.0: - raise ValueError, 'stdgamma: alpha must be > 0.0' - - if alpha > 1.0: - - # Uses R.C.H. Cheng, "The generation of Gamma - # variables with non-integral shape parameters", - # Applied Statistics, (1977), 26, No. 1, p71-74 - - while 1: - u1 = random() - u2 = random() - v = log(u1/(1.0-u1))/ainv - x = alpha*exp(v) - z = u1*u1*u2 - r = bbb+ccc*v-x - if r + SG_MAGICCONST - 4.5*z >= 0.0 or r >= log(z): - return x - - elif alpha == 1.0: - # expovariate(1) - u = random() - while u <= 1e-7: + def gammavariate(self, alpha, beta): + # beta times standard gamma + ainv = _sqrt(2.0 * alpha - 1.0) + return beta * self.stdgamma(alpha, ainv, alpha - LOG4, alpha + ainv) + + def stdgamma(self, alpha, ainv, bbb, ccc): + # ainv = sqrt(2 * alpha - 1) + # bbb = alpha - log(4) + # ccc = alpha + ainv + + random = self.random + if alpha <= 0.0: + raise ValueError, 'stdgamma: alpha must be > 0.0' + + if alpha > 1.0: + + # Uses R.C.H. Cheng, "The generation of Gamma + # variables with non-integral shape parameters", + # Applied Statistics, (1977), 26, No. 1, p71-74 + + while 1: + u1 = random() + u2 = random() + v = _log(u1/(1.0-u1))/ainv + x = alpha*_exp(v) + z = u1*u1*u2 + r = bbb+ccc*v-x + if r + SG_MAGICCONST - 4.5*z >= 0.0 or r >= _log(z): + return x + + elif alpha == 1.0: + # expovariate(1) u = random() - return -log(u) - - else: # alpha is between 0 and 1 (exclusive) - - # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle - - while 1: - u = random() - b = (e + alpha)/e - p = b*u - if p <= 1.0: - x = pow(p, 1.0/alpha) - else: - # p > 1 - x = -log((b-p)/alpha) - u1 = random() - if not (((p <= 1.0) and (u1 > exp(-x))) or - ((p > 1) and (u1 > pow(x, alpha - 1.0)))): - break - return x + while u <= 1e-7: + u = random() + return -_log(u) + + else: # alpha is between 0 and 1 (exclusive) + + # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle + + while 1: + u = random() + b = (_e + alpha)/_e + p = b*u + if p <= 1.0: + x = pow(p, 1.0/alpha) + else: + # p > 1 + x = -_log((b-p)/alpha) + u1 = random() + if not (((p <= 1.0) and (u1 > _exp(-x))) or + ((p > 1) and (u1 > pow(x, alpha - 1.0)))): + break + return x # -------------------- Gauss (faster alternative) -------------------- -gauss_next = None -def gauss(mu, sigma): - - # When x and y are two variables from [0, 1), uniformly - # distributed, then - # - # cos(2*pi*x)*sqrt(-2*log(1-y)) - # sin(2*pi*x)*sqrt(-2*log(1-y)) - # - # are two *independent* variables with normal distribution - # (mu = 0, sigma = 1). - # (Lambert Meertens) - # (corrected version; bug discovered by Mike Miller, fixed by LM) - - # Multithreading note: When two threads call this function - # simultaneously, it is possible that they will receive the - # same return value. The window is very small though. To - # avoid this, you have to use a lock around all calls. (I - # didn't want to slow this down in the serial case by using a - # lock here.) - - global gauss_next - - z = gauss_next - gauss_next = None - if z is None: - x2pi = random() * TWOPI - g2rad = sqrt(-2.0 * log(1.0 - random())) - z = cos(x2pi) * g2rad - gauss_next = sin(x2pi) * g2rad - - return mu + z*sigma + def gauss(self, mu, sigma): + + # When x and y are two variables from [0, 1), uniformly + # distributed, then + # + # cos(2*pi*x)*sqrt(-2*log(1-y)) + # sin(2*pi*x)*sqrt(-2*log(1-y)) + # + # are two *independent* variables with normal distribution + # (mu = 0, sigma = 1). + # (Lambert Meertens) + # (corrected version; bug discovered by Mike Miller, fixed by LM) + + # Multithreading note: When two threads call this function + # simultaneously, it is possible that they will receive the + # same return value. The window is very small though. To + # avoid this, you have to use a lock around all calls. (I + # didn't want to slow this down in the serial case by using a + # lock here.) + + random = self.random + z = self.gauss_next + self.gauss_next = None + if z is None: + x2pi = random() * TWOPI + g2rad = _sqrt(-2.0 * _log(1.0 - random())) + z = _cos(x2pi) * g2rad + self.gauss_next = _sin(x2pi) * g2rad + + return mu + z*sigma # -------------------- beta -------------------- -def betavariate(alpha, beta): + def betavariate(self, alpha, beta): - # Discrete Event Simulation in C, pp 87-88. + # Discrete Event Simulation in C, pp 87-88. - y = expovariate(alpha) - z = expovariate(1.0/beta) - return z/(y+z) + y = self.expovariate(alpha) + z = self.expovariate(1.0/beta) + return z/(y+z) # -------------------- Pareto -------------------- -def paretovariate(alpha): - # Jain, pg. 495 + def paretovariate(self, alpha): + # Jain, pg. 495 - u = random() - return 1.0 / pow(u, 1.0/alpha) + u = self.random() + return 1.0 / pow(u, 1.0/alpha) # -------------------- Weibull -------------------- -def weibullvariate(alpha, beta): - # Jain, pg. 499; bug fix courtesy Bill Arms + def weibullvariate(self, alpha, beta): + # Jain, pg. 499; bug fix courtesy Bill Arms - u = random() - return alpha * pow(-log(u), 1.0/beta) - -# -------------------- shuffle -------------------- -# Not quite a random distribution, but a standard algorithm. -# This implementation due to Tim Peters. - -def shuffle(x, random=random, int=int): - """x, random=random.random -> shuffle list x in place; return None. - - Optional arg random is a 0-argument function returning a random - float in [0.0, 1.0); by default, the standard random.random. - - Note that for even rather small len(x), the total number of - permutations of x is larger than the period of most random number - generators; this implies that "most" permutations of a long - sequence can never be generated. - """ - - for i in xrange(len(x)-1, 0, -1): - # 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] + u = self.random() + return alpha * pow(-_log(u), 1.0/beta) # -------------------- test program -------------------- -def test(N = 200): - print 'TWOPI =', TWOPI - print 'LOG4 =', LOG4 - print 'NV_MAGICCONST =', NV_MAGICCONST - print 'SG_MAGICCONST =', SG_MAGICCONST - test_generator(N, 'random()') - test_generator(N, 'normalvariate(0.0, 1.0)') - test_generator(N, 'lognormvariate(0.0, 1.0)') - test_generator(N, 'cunifvariate(0.0, 1.0)') - test_generator(N, 'expovariate(1.0)') - test_generator(N, 'vonmisesvariate(0.0, 1.0)') - test_generator(N, 'gammavariate(0.5, 1.0)') - test_generator(N, 'gammavariate(0.9, 1.0)') - test_generator(N, 'gammavariate(1.0, 1.0)') - test_generator(N, 'gammavariate(2.0, 1.0)') - test_generator(N, 'gammavariate(20.0, 1.0)') - test_generator(N, 'gammavariate(200.0, 1.0)') - test_generator(N, 'gauss(0.0, 1.0)') - test_generator(N, 'betavariate(3.0, 3.0)') - test_generator(N, 'paretovariate(1.0)') - test_generator(N, 'weibullvariate(1.0, 1.0)') - -def test_generator(n, funccall): +def _test_generator(n, funccall): import time print n, 'times', funccall code = compile(funccall, funccall, 'eval') @@ -356,9 +467,54 @@ def test_generator(n, funccall): t1 = time.time() print round(t1-t0, 3), 'sec,', avg = sum/n - stddev = sqrt(sqsum/n - avg*avg) + stddev = _sqrt(sqsum/n - avg*avg) print 'avg %g, stddev %g, min %g, max %g' % \ (avg, stddev, smallest, largest) +def _test(N=200): + print 'TWOPI =', TWOPI + print 'LOG4 =', LOG4 + print 'NV_MAGICCONST =', NV_MAGICCONST + print 'SG_MAGICCONST =', SG_MAGICCONST + _test_generator(N, 'random()') + _test_generator(N, 'normalvariate(0.0, 1.0)') + _test_generator(N, 'lognormvariate(0.0, 1.0)') + _test_generator(N, 'cunifvariate(0.0, 1.0)') + _test_generator(N, 'expovariate(1.0)') + _test_generator(N, 'vonmisesvariate(0.0, 1.0)') + _test_generator(N, 'gammavariate(0.5, 1.0)') + _test_generator(N, 'gammavariate(0.9, 1.0)') + _test_generator(N, 'gammavariate(1.0, 1.0)') + _test_generator(N, 'gammavariate(2.0, 1.0)') + _test_generator(N, 'gammavariate(20.0, 1.0)') + _test_generator(N, 'gammavariate(200.0, 1.0)') + _test_generator(N, 'gauss(0.0, 1.0)') + _test_generator(N, 'betavariate(3.0, 3.0)') + _test_generator(N, 'paretovariate(1.0)') + _test_generator(N, 'weibullvariate(1.0, 1.0)') + +# Initialize from current time. +_inst = Random() +seed = _inst.seed +random = _inst.random +uniform = _inst.uniform +randint = _inst.randint +choice = _inst.choice +randrange = _inst.randrange +shuffle = _inst.shuffle +normalvariate = _inst.normalvariate +lognormvariate = _inst.lognormvariate +cunifvariate = _inst.cunifvariate +expovariate = _inst.expovariate +vonmisesvariate = _inst.vonmisesvariate +gammavariate = _inst.gammavariate +stdgamma = _inst.stdgamma +gauss = _inst.gauss +betavariate = _inst.betavariate +paretovariate = _inst.paretovariate +weibullvariate = _inst.weibullvariate +getstate = _inst.getstate +setstate = _inst.setstate + if __name__ == '__main__': - test() + _test() @@ -1,3 +1,16 @@ +What's New in Python 2.1 alpha 2? +================================= +Core language, builtins, and interpreter + + +Standard library + +- random.py is now self-contained, and offers all the functionality of + the now-deprecated whrandom.py. See the docs for details. random.py + also supports new functions getstate() and setstate(), for saving + and restoring the internal state of all the generators. + + What's New in Python 2.1 alpha 1? ================================= |