mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									83125775e0
								
							
						
					
					
						commit
						d7b5e88e8e
					
				
					 3 changed files with 501 additions and 308 deletions
				
			
		
							
								
								
									
										710
									
								
								Lib/random.py
									
										
									
									
									
								
							
							
						
						
									
										710
									
								
								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 @@ | |||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| # Interfaces to replace remaining needs for importing whrandom | ||||
| # XXX TO DO: make the distribution functions below into methods. | ||||
| 
 | ||||
| def makeseed(a=None): | ||||
|     """Turn a hashable value into three seed values for whrandom.seed(). | ||||
| 
 | ||||
|     None or no argument returns (0, 0, 0), to seed from current time. | ||||
| 
 | ||||
|     """ | ||||
|     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) | ||||
| 
 | ||||
| def seed(a=None): | ||||
|     """Seed the default generator from any hashable value. | ||||
| 
 | ||||
|     None or no argument seeds from current time. | ||||
| 
 | ||||
|     """ | ||||
|     x, y, z = makeseed(a) | ||||
|     whrandom.seed(x, y, z) | ||||
| 
 | ||||
| class generator(whrandom.whrandom): | ||||
|     """Random generator class.""" | ||||
| 
 | ||||
|     def __init__(self, a=None): | ||||
|         """Constructor.  Seed from current time or hashable value.""" | ||||
|         self.seed(a) | ||||
| 
 | ||||
|     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) | ||||
| 
 | ||||
| def new_generator(a=None): | ||||
|     """Return a new random generator instance.""" | ||||
|     return generator(a) | ||||
| 
 | ||||
| # Housekeeping function to verify that magic constants have been | ||||
| # computed correctly | ||||
| 
 | ||||
| def verify(name, expected): | ||||
| 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) | ||||
|         raise ValueError( | ||||
|             "computed value for %s deviates too much " | ||||
|             "(computed %g, expected %g)" % (name, computed, expected)) | ||||
| 
 | ||||
| NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) | ||||
| _verify('NV_MAGICCONST', 1.71552776992141) | ||||
| 
 | ||||
| TWOPI = 2.0*_pi | ||||
| _verify('TWOPI', 6.28318530718) | ||||
| 
 | ||||
| LOG4 = _log(4.0) | ||||
| _verify('LOG4', 1.38629436111989) | ||||
| 
 | ||||
| SG_MAGICCONST = 1.0 + _log(4.5) | ||||
| _verify('SG_MAGICCONST', 2.50407739677627) | ||||
| 
 | ||||
| del _verify | ||||
| 
 | ||||
| # Translated by Guido van Rossum from C source provided by | ||||
| # Adrian Baddeley. | ||||
| 
 | ||||
| class Random: | ||||
| 
 | ||||
|     VERSION = 1     # used by getstate/setstate | ||||
| 
 | ||||
|     def __init__(self, x=None): | ||||
|         """Initialize an instance. | ||||
| 
 | ||||
|         Optional argument x controls seeding, as for Random.seed(). | ||||
|         """ | ||||
| 
 | ||||
|         self.seed(x) | ||||
|         self.gauss_next = None | ||||
| 
 | ||||
|     # Specific to Wichmann-Hill generator.  Subclasses wishing to use a | ||||
|     # different core generator should override seed(), random(),  getstate() | ||||
|     # and setstate(). | ||||
| 
 | ||||
|     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 | ||||
|     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. | ||||
| 
 | ||||
|     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)) | ||||
| 
 | ||||
| # -------------------- 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) | ||||
| 
 | ||||
|     return (mean + arc * (random() - 0.5)) % pi | ||||
| 
 | ||||
| # -------------------- exponential distribution -------------------- | ||||
| 
 | ||||
| def expovariate(lambd): | ||||
|     # lambd: rate lambd = 1/mean | ||||
|     # ('lambda' is a Python reserved word) | ||||
| 
 | ||||
|     u = random() | ||||
|     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 | ||||
| 
 | ||||
|     # 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. | ||||
| 
 | ||||
|     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) | ||||
| 
 | ||||
|     while 1: | ||||
|         u1 = random() | ||||
| 
 | ||||
|         z = cos(pi * u1) | ||||
|         f = (1.0 + r * z)/(r + z) | ||||
|         c = kappa * (r - f) | ||||
| 
 | ||||
|         u2 = random() | ||||
| 
 | ||||
|         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) | ||||
| 
 | ||||
|     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 | ||||
|         # 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() | ||||
|             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 | ||||
|             z = NV_MAGICCONST*(u1-0.5)/u2 | ||||
|             zz = z*z/4.0 | ||||
|             if zz <= -_log(u2): | ||||
|                 break | ||||
|         return mu + z*sigma | ||||
| 
 | ||||
|     elif alpha == 1.0: | ||||
|         # expovariate(1) | ||||
| # -------------------- lognormal distribution -------------------- | ||||
| 
 | ||||
|     def lognormvariate(self, mu, sigma): | ||||
|         return _exp(self.normalvariate(mu, sigma)) | ||||
| 
 | ||||
| # -------------------- circular uniform -------------------- | ||||
| 
 | ||||
|     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 * (self.random() - 0.5)) % _pi | ||||
| 
 | ||||
| # -------------------- exponential distribution -------------------- | ||||
| 
 | ||||
|     def expovariate(self, lambd): | ||||
|         # lambd: rate lambd = 1/mean | ||||
|         # ('lambda' is a Python reserved word) | ||||
| 
 | ||||
|         random = self.random | ||||
|         u = random() | ||||
|         while u <= 1e-7: | ||||
|             u = random() | ||||
|         return -log(u) | ||||
|         return -_log(u)/lambd | ||||
| 
 | ||||
|     else:   # alpha is between 0 and 1 (exclusive) | ||||
| # -------------------- von Mises distribution -------------------- | ||||
| 
 | ||||
|         # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle | ||||
|     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. | ||||
| 
 | ||||
|         # Thanks to Magnus Kessler for a correction to the | ||||
|         # implementation of step 4. | ||||
| 
 | ||||
|         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) | ||||
| 
 | ||||
|         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)))): | ||||
| 
 | ||||
|             z = _cos(_pi * u1) | ||||
|             f = (1.0 + r * z)/(r + z) | ||||
|             c = kappa * (r - f) | ||||
| 
 | ||||
|             u2 = random() | ||||
| 
 | ||||
|             if not (u2 >= c * (2.0 - c) and u2 > c * _exp(1.0 - c)): | ||||
|                 break | ||||
|         return x | ||||
| 
 | ||||
|         u3 = random() | ||||
|         if u3 > 0.5: | ||||
|             theta = (mu % TWOPI) + _acos(f) | ||||
|         else: | ||||
|             theta = (mu % TWOPI) - _acos(f) | ||||
| 
 | ||||
|         return theta | ||||
| 
 | ||||
| # -------------------- gamma distribution -------------------- | ||||
| 
 | ||||
|     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() | ||||
|             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): | ||||
|     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) | ||||
|         # 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.) | ||||
|         # 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 | ||||
|         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 | ||||
| 
 | ||||
|     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 | ||||
|         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() | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tim Peters
						Tim Peters