Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/random.py: 4%

358 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1"""Random variable generators. 

2 

3 integers 

4 -------- 

5 uniform within range 

6 

7 sequences 

8 --------- 

9 pick random element 

10 pick random sample 

11 pick weighted random sample 

12 generate random permutation 

13 

14 distributions on the real line: 

15 ------------------------------ 

16 uniform 

17 triangular 

18 normal (Gaussian) 

19 lognormal 

20 negative exponential 

21 gamma 

22 beta 

23 pareto 

24 Weibull 

25 

26 distributions on the circle (angles 0 to 2pi) 

27 --------------------------------------------- 

28 circular uniform 

29 von Mises 

30 

31General notes on the underlying Mersenne Twister core generator: 

32 

33* The period is 2**19937-1. 

34* It is one of the most extensively tested generators in existence. 

35* The random() method is implemented in C, executes in a single Python step, 

36 and is, therefore, threadsafe. 

37 

38""" 

39 

40from warnings import warn as _warn 

41from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil 

42from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin 

43from os import urandom as _urandom 

44from _collections_abc import Set as _Set, Sequence as _Sequence 

45from itertools import accumulate as _accumulate, repeat as _repeat 

46from bisect import bisect as _bisect 

47import os as _os 

48 

49try: 

50 # hashlib is pretty heavy to load, try lean internal module first 

51 from _sha512 import sha512 as _sha512 

52except ImportError: 

53 # fallback to official implementation 

54 from hashlib import sha512 as _sha512 

55 

56 

57__all__ = ["Random","seed","random","uniform","randint","choice","sample", 

58 "randrange","shuffle","normalvariate","lognormvariate", 

59 "expovariate","vonmisesvariate","gammavariate","triangular", 

60 "gauss","betavariate","paretovariate","weibullvariate", 

61 "getstate","setstate", "getrandbits", "choices", 

62 "SystemRandom"] 

63 

64NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) 

65TWOPI = 2.0*_pi 

66LOG4 = _log(4.0) 

67SG_MAGICCONST = 1.0 + _log(4.5) 

68BPF = 53 # Number of bits in a float 

69RECIP_BPF = 2**-BPF 

70 

71 

72# Translated by Guido van Rossum from C source provided by 

73# Adrian Baddeley. Adapted by Raymond Hettinger for use with 

74# the Mersenne Twister and os.urandom() core generators. 

75 

76import _random 

77 

78class Random(_random.Random): 

79 """Random number generator base class used by bound module functions. 

80 

81 Used to instantiate instances of Random to get generators that don't 

82 share state. 

83 

84 Class Random can also be subclassed if you want to use a different basic 

85 generator of your own devising: in that case, override the following 

86 methods: random(), seed(), getstate(), and setstate(). 

87 Optionally, implement a getrandbits() method so that randrange() 

88 can cover arbitrarily large ranges. 

89 

90 """ 

91 

92 VERSION = 3 # used by getstate/setstate 

93 

94 def __init__(self, x=None): 

95 """Initialize an instance. 

96 

97 Optional argument x controls seeding, as for Random.seed(). 

98 """ 

99 

100 self.seed(x) 

101 self.gauss_next = None 

102 

103 def __init_subclass__(cls, /, **kwargs): 

104 """Control how subclasses generate random integers. 

105 

106 The algorithm a subclass can use depends on the random() and/or 

107 getrandbits() implementation available to it and determines 

108 whether it can generate random integers from arbitrarily large 

109 ranges. 

110 """ 

111 

112 for c in cls.__mro__: 

113 if '_randbelow' in c.__dict__: 

114 # just inherit it 

115 break 

116 if 'getrandbits' in c.__dict__: 

117 cls._randbelow = cls._randbelow_with_getrandbits 

118 break 

119 if 'random' in c.__dict__: 

120 cls._randbelow = cls._randbelow_without_getrandbits 

121 break 

122 

123 def seed(self, a=None, version=2): 

124 """Initialize internal state from hashable object. 

125 

126 None or no argument seeds from current time or from an operating 

127 system specific randomness source if available. 

128 

129 If *a* is an int, all bits are used. 

130 

131 For version 2 (the default), all of the bits are used if *a* is a str, 

132 bytes, or bytearray. For version 1 (provided for reproducing random 

133 sequences from older versions of Python), the algorithm for str and 

134 bytes generates a narrower range of seeds. 

135 

136 """ 

137 

138 if version == 1 and isinstance(a, (str, bytes)): 

139 a = a.decode('latin-1') if isinstance(a, bytes) else a 

140 x = ord(a[0]) << 7 if a else 0 

141 for c in map(ord, a): 

142 x = ((1000003 * x) ^ c) & 0xFFFFFFFFFFFFFFFF 

143 x ^= len(a) 

144 a = -2 if x == -1 else x 

145 

146 if version == 2 and isinstance(a, (str, bytes, bytearray)): 

147 if isinstance(a, str): 

148 a = a.encode() 

149 a += _sha512(a).digest() 

150 a = int.from_bytes(a, 'big') 

151 

152 super().seed(a) 

153 self.gauss_next = None 

154 

155 def getstate(self): 

156 """Return internal state; can be passed to setstate() later.""" 

157 return self.VERSION, super().getstate(), self.gauss_next 

158 

159 def setstate(self, state): 

160 """Restore internal state from object returned by getstate().""" 

161 version = state[0] 

162 if version == 3: 

163 version, internalstate, self.gauss_next = state 

164 super().setstate(internalstate) 

165 elif version == 2: 

166 version, internalstate, self.gauss_next = state 

167 # In version 2, the state was saved as signed ints, which causes 

168 # inconsistencies between 32/64-bit systems. The state is 

169 # really unsigned 32-bit ints, so we convert negative ints from 

170 # version 2 to positive longs for version 3. 

171 try: 

172 internalstate = tuple(x % (2**32) for x in internalstate) 

173 except ValueError as e: 

174 raise TypeError from e 

175 super().setstate(internalstate) 

176 else: 

177 raise ValueError("state with version %s passed to " 

178 "Random.setstate() of version %s" % 

179 (version, self.VERSION)) 

180 

181## ---- Methods below this point do not need to be overridden when 

182## ---- subclassing for the purpose of using a different core generator. 

183 

184## -------------------- pickle support ------------------- 

185 

186 # Issue 17489: Since __reduce__ was defined to fix #759889 this is no 

187 # longer called; we leave it here because it has been here since random was 

188 # rewritten back in 2001 and why risk breaking something. 

189 def __getstate__(self): # for pickle 

190 return self.getstate() 

191 

192 def __setstate__(self, state): # for pickle 

193 self.setstate(state) 

194 

195 def __reduce__(self): 

196 return self.__class__, (), self.getstate() 

197 

198## -------------------- integer methods ------------------- 

199 

200 def randrange(self, start, stop=None, step=1, _int=int): 

201 """Choose a random item from range(start, stop[, step]). 

202 

203 This fixes the problem with randint() which includes the 

204 endpoint; in Python this is usually not what you want. 

205 

206 """ 

207 

208 # This code is a bit messy to make it fast for the 

209 # common case while still doing adequate error checking. 

210 istart = _int(start) 

211 if istart != start: 

212 raise ValueError("non-integer arg 1 for randrange()") 

213 if stop is None: 

214 if istart > 0: 

215 return self._randbelow(istart) 

216 raise ValueError("empty range for randrange()") 

217 

218 # stop argument supplied. 

219 istop = _int(stop) 

220 if istop != stop: 

221 raise ValueError("non-integer stop for randrange()") 

222 width = istop - istart 

223 if step == 1 and width > 0: 

224 return istart + self._randbelow(width) 

225 if step == 1: 

226 raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) 

227 

228 # Non-unit step argument supplied. 

229 istep = _int(step) 

230 if istep != step: 

231 raise ValueError("non-integer step for randrange()") 

232 if istep > 0: 

233 n = (width + istep - 1) // istep 

234 elif istep < 0: 

235 n = (width + istep + 1) // istep 

236 else: 

237 raise ValueError("zero step for randrange()") 

238 

239 if n <= 0: 

240 raise ValueError("empty range for randrange()") 

241 

242 return istart + istep*self._randbelow(n) 

243 

244 def randint(self, a, b): 

245 """Return random integer in range [a, b], including both end points. 

246 """ 

247 

248 return self.randrange(a, b+1) 

249 

250 def _randbelow_with_getrandbits(self, n): 

251 "Return a random int in the range [0,n). Raises ValueError if n==0." 

252 

253 getrandbits = self.getrandbits 

254 k = n.bit_length() # don't use (n-1) here because n can be 1 

255 r = getrandbits(k) # 0 <= r < 2**k 

256 while r >= n: 

257 r = getrandbits(k) 

258 return r 

259 

260 def _randbelow_without_getrandbits(self, n, int=int, maxsize=1<<BPF): 

261 """Return a random int in the range [0,n). Raises ValueError if n==0. 

262 

263 The implementation does not use getrandbits, but only random. 

264 """ 

265 

266 random = self.random 

267 if n >= maxsize: 

268 _warn("Underlying random() generator does not supply \n" 

269 "enough bits to choose from a population range this large.\n" 

270 "To remove the range limitation, add a getrandbits() method.") 

271 return int(random() * n) 

272 if n == 0: 

273 raise ValueError("Boundary cannot be zero") 

274 rem = maxsize % n 

275 limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0 

276 r = random() 

277 while r >= limit: 

278 r = random() 

279 return int(r*maxsize) % n 

280 

281 _randbelow = _randbelow_with_getrandbits 

282 

283## -------------------- sequence methods ------------------- 

284 

285 def choice(self, seq): 

286 """Choose a random element from a non-empty sequence.""" 

287 try: 

288 i = self._randbelow(len(seq)) 

289 except ValueError: 

290 raise IndexError('Cannot choose from an empty sequence') from None 

291 return seq[i] 

292 

293 def shuffle(self, x, random=None): 

294 """Shuffle list x in place, and return None. 

295 

296 Optional argument random is a 0-argument function returning a 

297 random float in [0.0, 1.0); if it is the default None, the 

298 standard random.random will be used. 

299 

300 """ 

301 

302 if random is None: 

303 randbelow = self._randbelow 

304 for i in reversed(range(1, len(x))): 

305 # pick an element in x[:i+1] with which to exchange x[i] 

306 j = randbelow(i+1) 

307 x[i], x[j] = x[j], x[i] 

308 else: 

309 _int = int 

310 for i in reversed(range(1, len(x))): 

311 # pick an element in x[:i+1] with which to exchange x[i] 

312 j = _int(random() * (i+1)) 

313 x[i], x[j] = x[j], x[i] 

314 

315 def sample(self, population, k): 

316 """Chooses k unique random elements from a population sequence or set. 

317 

318 Returns a new list containing elements from the population while 

319 leaving the original population unchanged. The resulting list is 

320 in selection order so that all sub-slices will also be valid random 

321 samples. This allows raffle winners (the sample) to be partitioned 

322 into grand prize and second place winners (the subslices). 

323 

324 Members of the population need not be hashable or unique. If the 

325 population contains repeats, then each occurrence is a possible 

326 selection in the sample. 

327 

328 To choose a sample in a range of integers, use range as an argument. 

329 This is especially fast and space efficient for sampling from a 

330 large population: sample(range(10000000), 60) 

331 """ 

332 

333 # Sampling without replacement entails tracking either potential 

334 # selections (the pool) in a list or previous selections in a set. 

335 

336 # When the number of selections is small compared to the 

337 # population, then tracking selections is efficient, requiring 

338 # only a small set and an occasional reselection. For 

339 # a larger number of selections, the pool tracking method is 

340 # preferred since the list takes less space than the 

341 # set and it doesn't suffer from frequent reselections. 

342 

343 # The number of calls to _randbelow() is kept at or near k, the 

344 # theoretical minimum. This is important because running time 

345 # is dominated by _randbelow() and because it extracts the 

346 # least entropy from the underlying random number generators. 

347 

348 # Memory requirements are kept to the smaller of a k-length 

349 # set or an n-length list. 

350 

351 # There are other sampling algorithms that do not require 

352 # auxiliary memory, but they were rejected because they made 

353 # too many calls to _randbelow(), making them slower and 

354 # causing them to eat more entropy than necessary. 

355 

356 if isinstance(population, _Set): 

357 population = tuple(population) 

358 if not isinstance(population, _Sequence): 

359 raise TypeError("Population must be a sequence or set. For dicts, use list(d).") 

360 randbelow = self._randbelow 

361 n = len(population) 

362 if not 0 <= k <= n: 

363 raise ValueError("Sample larger than population or is negative") 

364 result = [None] * k 

365 setsize = 21 # size of a small set minus size of an empty list 

366 if k > 5: 

367 setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets 

368 if n <= setsize: 

369 # An n-length list is smaller than a k-length set 

370 pool = list(population) 

371 for i in range(k): # invariant: non-selected at [0,n-i) 

372 j = randbelow(n-i) 

373 result[i] = pool[j] 

374 pool[j] = pool[n-i-1] # move non-selected item into vacancy 

375 else: 

376 selected = set() 

377 selected_add = selected.add 

378 for i in range(k): 

379 j = randbelow(n) 

380 while j in selected: 

381 j = randbelow(n) 

382 selected_add(j) 

383 result[i] = population[j] 

384 return result 

385 

386 def choices(self, population, weights=None, *, cum_weights=None, k=1): 

387 """Return a k sized list of population elements chosen with replacement. 

388 

389 If the relative weights or cumulative weights are not specified, 

390 the selections are made with equal probability. 

391 

392 """ 

393 random = self.random 

394 n = len(population) 

395 if cum_weights is None: 

396 if weights is None: 

397 _int = int 

398 n += 0.0 # convert to float for a small speed improvement 

399 return [population[_int(random() * n)] for i in _repeat(None, k)] 

400 cum_weights = list(_accumulate(weights)) 

401 elif weights is not None: 

402 raise TypeError('Cannot specify both weights and cumulative weights') 

403 if len(cum_weights) != n: 

404 raise ValueError('The number of weights does not match the population') 

405 bisect = _bisect 

406 total = cum_weights[-1] + 0.0 # convert to float 

407 hi = n - 1 

408 return [population[bisect(cum_weights, random() * total, 0, hi)] 

409 for i in _repeat(None, k)] 

410 

411## -------------------- real-valued distributions ------------------- 

412 

413## -------------------- uniform distribution ------------------- 

414 

415 def uniform(self, a, b): 

416 "Get a random number in the range [a, b) or [a, b] depending on rounding." 

417 return a + (b-a) * self.random() 

418 

419## -------------------- triangular -------------------- 

420 

421 def triangular(self, low=0.0, high=1.0, mode=None): 

422 """Triangular distribution. 

423 

424 Continuous distribution bounded by given lower and upper limits, 

425 and having a given mode value in-between. 

426 

427 http://en.wikipedia.org/wiki/Triangular_distribution 

428 

429 """ 

430 u = self.random() 

431 try: 

432 c = 0.5 if mode is None else (mode - low) / (high - low) 

433 except ZeroDivisionError: 

434 return low 

435 if u > c: 

436 u = 1.0 - u 

437 c = 1.0 - c 

438 low, high = high, low 

439 return low + (high - low) * _sqrt(u * c) 

440 

441## -------------------- normal distribution -------------------- 

442 

443 def normalvariate(self, mu, sigma): 

444 """Normal distribution. 

445 

446 mu is the mean, and sigma is the standard deviation. 

447 

448 """ 

449 # mu = mean, sigma = standard deviation 

450 

451 # Uses Kinderman and Monahan method. Reference: Kinderman, 

452 # A.J. and Monahan, J.F., "Computer generation of random 

453 # variables using the ratio of uniform deviates", ACM Trans 

454 # Math Software, 3, (1977), pp257-260. 

455 

456 random = self.random 

457 while 1: 

458 u1 = random() 

459 u2 = 1.0 - random() 

460 z = NV_MAGICCONST*(u1-0.5)/u2 

461 zz = z*z/4.0 

462 if zz <= -_log(u2): 

463 break 

464 return mu + z*sigma 

465 

466## -------------------- lognormal distribution -------------------- 

467 

468 def lognormvariate(self, mu, sigma): 

469 """Log normal distribution. 

470 

471 If you take the natural logarithm of this distribution, you'll get a 

472 normal distribution with mean mu and standard deviation sigma. 

473 mu can have any value, and sigma must be greater than zero. 

474 

475 """ 

476 return _exp(self.normalvariate(mu, sigma)) 

477 

478## -------------------- exponential distribution -------------------- 

479 

480 def expovariate(self, lambd): 

481 """Exponential distribution. 

482 

483 lambd is 1.0 divided by the desired mean. It should be 

484 nonzero. (The parameter would be called "lambda", but that is 

485 a reserved word in Python.) Returned values range from 0 to 

486 positive infinity if lambd is positive, and from negative 

487 infinity to 0 if lambd is negative. 

488 

489 """ 

490 # lambd: rate lambd = 1/mean 

491 # ('lambda' is a Python reserved word) 

492 

493 # we use 1-random() instead of random() to preclude the 

494 # possibility of taking the log of zero. 

495 return -_log(1.0 - self.random())/lambd 

496 

497## -------------------- von Mises distribution -------------------- 

498 

499 def vonmisesvariate(self, mu, kappa): 

500 """Circular data distribution. 

501 

502 mu is the mean angle, expressed in radians between 0 and 2*pi, and 

503 kappa is the concentration parameter, which must be greater than or 

504 equal to zero. If kappa is equal to zero, this distribution reduces 

505 to a uniform random angle over the range 0 to 2*pi. 

506 

507 """ 

508 # mu: mean angle (in radians between 0 and 2*pi) 

509 # kappa: concentration parameter kappa (>= 0) 

510 # if kappa = 0 generate uniform random angle 

511 

512 # Based upon an algorithm published in: Fisher, N.I., 

513 # "Statistical Analysis of Circular Data", Cambridge 

514 # University Press, 1993. 

515 

516 # Thanks to Magnus Kessler for a correction to the 

517 # implementation of step 4. 

518 

519 random = self.random 

520 if kappa <= 1e-6: 

521 return TWOPI * random() 

522 

523 s = 0.5 / kappa 

524 r = s + _sqrt(1.0 + s * s) 

525 

526 while 1: 

527 u1 = random() 

528 z = _cos(_pi * u1) 

529 

530 d = z / (r + z) 

531 u2 = random() 

532 if u2 < 1.0 - d * d or u2 <= (1.0 - d) * _exp(d): 

533 break 

534 

535 q = 1.0 / r 

536 f = (q + z) / (1.0 + q * z) 

537 u3 = random() 

538 if u3 > 0.5: 

539 theta = (mu + _acos(f)) % TWOPI 

540 else: 

541 theta = (mu - _acos(f)) % TWOPI 

542 

543 return theta 

544 

545## -------------------- gamma distribution -------------------- 

546 

547 def gammavariate(self, alpha, beta): 

548 """Gamma distribution. Not the gamma function! 

549 

550 Conditions on the parameters are alpha > 0 and beta > 0. 

551 

552 The probability distribution function is: 

553 

554 x ** (alpha - 1) * math.exp(-x / beta) 

555 pdf(x) = -------------------------------------- 

556 math.gamma(alpha) * beta ** alpha 

557 

558 """ 

559 

560 # alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2 

561 

562 # Warning: a few older sources define the gamma distribution in terms 

563 # of alpha > -1.0 

564 if alpha <= 0.0 or beta <= 0.0: 

565 raise ValueError('gammavariate: alpha and beta must be > 0.0') 

566 

567 random = self.random 

568 if alpha > 1.0: 

569 

570 # Uses R.C.H. Cheng, "The generation of Gamma 

571 # variables with non-integral shape parameters", 

572 # Applied Statistics, (1977), 26, No. 1, p71-74 

573 

574 ainv = _sqrt(2.0 * alpha - 1.0) 

575 bbb = alpha - LOG4 

576 ccc = alpha + ainv 

577 

578 while 1: 

579 u1 = random() 

580 if not 1e-7 < u1 < .9999999: 

581 continue 

582 u2 = 1.0 - random() 

583 v = _log(u1/(1.0-u1))/ainv 

584 x = alpha*_exp(v) 

585 z = u1*u1*u2 

586 r = bbb+ccc*v-x 

587 if r + SG_MAGICCONST - 4.5*z >= 0.0 or r >= _log(z): 

588 return x * beta 

589 

590 elif alpha == 1.0: 

591 # expovariate(1/beta) 

592 return -_log(1.0 - random()) * beta 

593 

594 else: # alpha is between 0 and 1 (exclusive) 

595 

596 # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle 

597 

598 while 1: 

599 u = random() 

600 b = (_e + alpha)/_e 

601 p = b*u 

602 if p <= 1.0: 

603 x = p ** (1.0/alpha) 

604 else: 

605 x = -_log((b-p)/alpha) 

606 u1 = random() 

607 if p > 1.0: 

608 if u1 <= x ** (alpha - 1.0): 

609 break 

610 elif u1 <= _exp(-x): 

611 break 

612 return x * beta 

613 

614## -------------------- Gauss (faster alternative) -------------------- 

615 

616 def gauss(self, mu, sigma): 

617 """Gaussian distribution. 

618 

619 mu is the mean, and sigma is the standard deviation. This is 

620 slightly faster than the normalvariate() function. 

621 

622 Not thread-safe without a lock around calls. 

623 

624 """ 

625 

626 # When x and y are two variables from [0, 1), uniformly 

627 # distributed, then 

628 # 

629 # cos(2*pi*x)*sqrt(-2*log(1-y)) 

630 # sin(2*pi*x)*sqrt(-2*log(1-y)) 

631 # 

632 # are two *independent* variables with normal distribution 

633 # (mu = 0, sigma = 1). 

634 # (Lambert Meertens) 

635 # (corrected version; bug discovered by Mike Miller, fixed by LM) 

636 

637 # Multithreading note: When two threads call this function 

638 # simultaneously, it is possible that they will receive the 

639 # same return value. The window is very small though. To 

640 # avoid this, you have to use a lock around all calls. (I 

641 # didn't want to slow this down in the serial case by using a 

642 # lock here.) 

643 

644 random = self.random 

645 z = self.gauss_next 

646 self.gauss_next = None 

647 if z is None: 

648 x2pi = random() * TWOPI 

649 g2rad = _sqrt(-2.0 * _log(1.0 - random())) 

650 z = _cos(x2pi) * g2rad 

651 self.gauss_next = _sin(x2pi) * g2rad 

652 

653 return mu + z*sigma 

654 

655## -------------------- beta -------------------- 

656## See 

657## http://mail.python.org/pipermail/python-bugs-list/2001-January/003752.html 

658## for Ivan Frohne's insightful analysis of why the original implementation: 

659## 

660## def betavariate(self, alpha, beta): 

661## # Discrete Event Simulation in C, pp 87-88. 

662## 

663## y = self.expovariate(alpha) 

664## z = self.expovariate(1.0/beta) 

665## return z/(y+z) 

666## 

667## was dead wrong, and how it probably got that way. 

668 

669 def betavariate(self, alpha, beta): 

670 """Beta distribution. 

671 

672 Conditions on the parameters are alpha > 0 and beta > 0. 

673 Returned values range between 0 and 1. 

674 

675 """ 

676 

677 # This version due to Janne Sinkkonen, and matches all the std 

678 # texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution"). 

679 y = self.gammavariate(alpha, 1.0) 

680 if y == 0: 

681 return 0.0 

682 else: 

683 return y / (y + self.gammavariate(beta, 1.0)) 

684 

685## -------------------- Pareto -------------------- 

686 

687 def paretovariate(self, alpha): 

688 """Pareto distribution. alpha is the shape parameter.""" 

689 # Jain, pg. 495 

690 

691 u = 1.0 - self.random() 

692 return 1.0 / u ** (1.0/alpha) 

693 

694## -------------------- Weibull -------------------- 

695 

696 def weibullvariate(self, alpha, beta): 

697 """Weibull distribution. 

698 

699 alpha is the scale parameter and beta is the shape parameter. 

700 

701 """ 

702 # Jain, pg. 499; bug fix courtesy Bill Arms 

703 

704 u = 1.0 - self.random() 

705 return alpha * (-_log(u)) ** (1.0/beta) 

706 

707## --------------- Operating System Random Source ------------------ 

708 

709class SystemRandom(Random): 

710 """Alternate random number generator using sources provided 

711 by the operating system (such as /dev/urandom on Unix or 

712 CryptGenRandom on Windows). 

713 

714 Not available on all systems (see os.urandom() for details). 

715 """ 

716 

717 def random(self): 

718 """Get the next random number in the range [0.0, 1.0).""" 

719 return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF 

720 

721 def getrandbits(self, k): 

722 """getrandbits(k) -> x. Generates an int with k random bits.""" 

723 if k <= 0: 

724 raise ValueError('number of bits must be greater than zero') 

725 numbytes = (k + 7) // 8 # bits / 8 and rounded up 

726 x = int.from_bytes(_urandom(numbytes), 'big') 

727 return x >> (numbytes * 8 - k) # trim excess bits 

728 

729 def seed(self, *args, **kwds): 

730 "Stub method. Not used for a system random number generator." 

731 return None 

732 

733 def _notimplemented(self, *args, **kwds): 

734 "Method should not be called for a system random number generator." 

735 raise NotImplementedError('System entropy source does not have state.') 

736 getstate = setstate = _notimplemented 

737 

738## -------------------- test program -------------------- 

739 

740def _test_generator(n, func, args): 

741 import time 

742 print(n, 'times', func.__name__) 

743 total = 0.0 

744 sqsum = 0.0 

745 smallest = 1e10 

746 largest = -1e10 

747 t0 = time.perf_counter() 

748 for i in range(n): 

749 x = func(*args) 

750 total += x 

751 sqsum = sqsum + x*x 

752 smallest = min(x, smallest) 

753 largest = max(x, largest) 

754 t1 = time.perf_counter() 

755 print(round(t1-t0, 3), 'sec,', end=' ') 

756 avg = total/n 

757 stddev = _sqrt(sqsum/n - avg*avg) 

758 print('avg %g, stddev %g, min %g, max %g\n' % \ 

759 (avg, stddev, smallest, largest)) 

760 

761 

762def _test(N=2000): 

763 _test_generator(N, random, ()) 

764 _test_generator(N, normalvariate, (0.0, 1.0)) 

765 _test_generator(N, lognormvariate, (0.0, 1.0)) 

766 _test_generator(N, vonmisesvariate, (0.0, 1.0)) 

767 _test_generator(N, gammavariate, (0.01, 1.0)) 

768 _test_generator(N, gammavariate, (0.1, 1.0)) 

769 _test_generator(N, gammavariate, (0.1, 2.0)) 

770 _test_generator(N, gammavariate, (0.5, 1.0)) 

771 _test_generator(N, gammavariate, (0.9, 1.0)) 

772 _test_generator(N, gammavariate, (1.0, 1.0)) 

773 _test_generator(N, gammavariate, (2.0, 1.0)) 

774 _test_generator(N, gammavariate, (20.0, 1.0)) 

775 _test_generator(N, gammavariate, (200.0, 1.0)) 

776 _test_generator(N, gauss, (0.0, 1.0)) 

777 _test_generator(N, betavariate, (3.0, 3.0)) 

778 _test_generator(N, triangular, (0.0, 1.0, 1.0/3.0)) 

779 

780# Create one instance, seeded from current time, and export its methods 

781# as module-level functions. The functions share state across all uses 

782#(both in the user's code and in the Python libraries), but that's fine 

783# for most programs and is easier for the casual user than making them 

784# instantiate their own Random() instance. 

785 

786_inst = Random() 

787seed = _inst.seed 

788random = _inst.random 

789uniform = _inst.uniform 

790triangular = _inst.triangular 

791randint = _inst.randint 

792choice = _inst.choice 

793randrange = _inst.randrange 

794sample = _inst.sample 

795shuffle = _inst.shuffle 

796choices = _inst.choices 

797normalvariate = _inst.normalvariate 

798lognormvariate = _inst.lognormvariate 

799expovariate = _inst.expovariate 

800vonmisesvariate = _inst.vonmisesvariate 

801gammavariate = _inst.gammavariate 

802gauss = _inst.gauss 

803betavariate = _inst.betavariate 

804paretovariate = _inst.paretovariate 

805weibullvariate = _inst.weibullvariate 

806getstate = _inst.getstate 

807setstate = _inst.setstate 

808getrandbits = _inst.getrandbits 

809 

810if hasattr(_os, "fork"): 

811 _os.register_at_fork(after_in_child=_inst.seed) 

812 

813 

814if __name__ == '__main__': 

815 _test()