random.py
来自「mallet是自然语言处理、机器学习领域的一个开源项目。」· Python 代码 · 共 780 行 · 第 1/2 页
PY
780 行
""" # 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 = 1.0 - 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(self, mu, sigma): """Log normal distribution. If you take the natural logarithm of this distribution, you'll get a normal distribution with mean mu and standard deviation sigma. mu can have any value, and sigma must be greater than zero. """ return _exp(self.normalvariate(mu, sigma))## -------------------- circular uniform -------------------- def cunifvariate(self, mean, arc): """Circular uniform distribution. mean is the mean angle, and arc is the range of the distribution, centered around the mean angle. Both values must be expressed in radians. Returned values range between mean - arc/2 and mean + arc/2 and are normalized to between 0 and pi. Deprecated in version 2.3. Use: (mean + arc * (Random.random() - 0.5)) % Math.pi """ # 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): """Exponential distribution. lambd is 1.0 divided by the desired mean. (The parameter would be called "lambda", but that is a reserved word in Python.) Returned values range from 0 to positive infinity. """ # 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)/lambd## -------------------- von Mises distribution -------------------- def vonmisesvariate(self, mu, kappa): """Circular data distribution. mu is the mean angle, expressed in radians between 0 and 2*pi, and kappa is the concentration parameter, which must be greater than or equal to zero. If kappa is equal to zero, this distribution reduces to a uniform random angle over the range 0 to 2*pi. """ # 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: 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 -------------------- def gammavariate(self, alpha, beta): """Gamma distribution. Not the gamma function! Conditions on the parameters are alpha > 0 and beta > 0. """ # alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2 # Warning: a few older sources define the gamma distribution in terms # of alpha > -1.0 if alpha <= 0.0 or beta <= 0.0: raise ValueError, 'gammavariate: alpha and beta must be > 0.0' random = self.random 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 ainv = _sqrt(2.0 * alpha - 1.0) bbb = alpha - LOG4 ccc = alpha + ainv while 1: u1 = random() if not 1e-7 < u1 < .9999999: continue u2 = 1.0 - 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 * beta elif alpha == 1.0: # expovariate(1) u = random() while u <= 1e-7: u = random() return -_log(u) * beta 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 * beta def stdgamma(self, alpha, ainv, bbb, ccc): # This method was (and shall remain) undocumented. # This method is deprecated # for the following reasons: # 1. Returns same as .gammavariate(alpha, 1.0) # 2. Requires caller to provide 3 extra arguments # that are functions of alpha anyway # 3. Can't be used for alpha < 0.5 # ainv = sqrt(2 * alpha - 1) # bbb = alpha - log(4) # ccc = alpha + ainv import warnings warnings.warn("The stdgamma function is deprecated; " "use gammavariate() instead", DeprecationWarning) return self.gammavariate(alpha, 1.0)## -------------------- Gauss (faster alternative) -------------------- def gauss(self, mu, sigma): """Gaussian distribution. mu is the mean, and sigma is the standard deviation. This is slightly faster than the normalvariate() function. Not thread-safe without a lock around calls. """ # 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 --------------------## See## http://sourceforge.net/bugs/?func=detailbug&bug_id=130030&group_id=5470## for Ivan Frohne's insightful analysis of why the original implementation:#### def betavariate(self, alpha, beta):## # Discrete Event Simulation in C, pp 87-88.#### y = self.expovariate(alpha)## z = self.expovariate(1.0/beta)## return z/(y+z)#### was dead wrong, and how it probably got that way. def betavariate(self, alpha, beta): """Beta distribution. Conditions on the parameters are alpha > -1 and beta} > -1. Returned values range between 0 and 1. """ # This version due to Janne Sinkkonen, and matches all the std # texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution"). y = self.gammavariate(alpha, 1.) if y == 0: return 0.0 else: return y / (y + self.gammavariate(beta, 1.))## -------------------- Pareto -------------------- def paretovariate(self, alpha): """Pareto distribution. alpha is the shape parameter.""" # Jain, pg. 495 u = 1.0 - self.random() return 1.0 / pow(u, 1.0/alpha)## -------------------- Weibull -------------------- def weibullvariate(self, alpha, beta): """Weibull distribution. alpha is the scale parameter and beta is the shape parameter. """ # Jain, pg. 499; bug fix courtesy Bill Arms u = 1.0 - self.random() return alpha * pow(-_log(u), 1.0/beta)## -------------------- test program --------------------def _test_generator(n, funccall): import time print n, 'times', funccall code = compile(funccall, funccall, 'eval') sum = 0.0 sqsum = 0.0 smallest = 1e10 largest = -1e10 t0 = time.time() for i in range(n): x = eval(code) sum = sum + x sqsum = sqsum + x*x smallest = min(x, smallest) largest = max(x, largest) t1 = time.time() print round(t1-t0, 3), 'sec,', avg = sum/n stddev = _sqrt(sqsum/n - avg*avg) print 'avg %g, stddev %g, min %g, max %g' % \ (avg, stddev, smallest, largest)def _test(N=20000): 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.01, 1.0)') _test_generator(N, 'gammavariate(0.1, 1.0)') _test_generator(N, 'gammavariate(0.1, 2.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)') # 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)`)# Create one instance, seeded from current time, and export its methods# as module-level functions. The functions are not threadsafe, and state# is shared across all uses (both in the user's code and in the Python# libraries), but that's fine for most programs and is easier for the# casual user than making them instantiate their own Random() instance._inst = Random()seed = _inst.seedrandom = _inst.randomuniform = _inst.uniformrandint = _inst.randintchoice = _inst.choicerandrange = _inst.randrangeshuffle = _inst.shufflenormalvariate = _inst.normalvariatelognormvariate = _inst.lognormvariatecunifvariate = _inst.cunifvariateexpovariate = _inst.expovariatevonmisesvariate = _inst.vonmisesvariategammavariate = _inst.gammavariatestdgamma = _inst.stdgammagauss = _inst.gaussbetavariate = _inst.betavariateparetovariate = _inst.paretovariateweibullvariate = _inst.weibullvariategetstate = _inst.getstatesetstate = _inst.setstatejumpahead = _inst.jumpaheadwhseed = _inst.whseedif __name__ == '__main__': _test()
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?