17db96d56Sopenharmony_ci"""Random variable generators. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci bytes 47db96d56Sopenharmony_ci ----- 57db96d56Sopenharmony_ci uniform bytes (values between 0 and 255) 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ci integers 87db96d56Sopenharmony_ci -------- 97db96d56Sopenharmony_ci uniform within range 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci sequences 127db96d56Sopenharmony_ci --------- 137db96d56Sopenharmony_ci pick random element 147db96d56Sopenharmony_ci pick random sample 157db96d56Sopenharmony_ci pick weighted random sample 167db96d56Sopenharmony_ci generate random permutation 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ci distributions on the real line: 197db96d56Sopenharmony_ci ------------------------------ 207db96d56Sopenharmony_ci uniform 217db96d56Sopenharmony_ci triangular 227db96d56Sopenharmony_ci normal (Gaussian) 237db96d56Sopenharmony_ci lognormal 247db96d56Sopenharmony_ci negative exponential 257db96d56Sopenharmony_ci gamma 267db96d56Sopenharmony_ci beta 277db96d56Sopenharmony_ci pareto 287db96d56Sopenharmony_ci Weibull 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci distributions on the circle (angles 0 to 2pi) 317db96d56Sopenharmony_ci --------------------------------------------- 327db96d56Sopenharmony_ci circular uniform 337db96d56Sopenharmony_ci von Mises 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ciGeneral notes on the underlying Mersenne Twister core generator: 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ci* The period is 2**19937-1. 387db96d56Sopenharmony_ci* It is one of the most extensively tested generators in existence. 397db96d56Sopenharmony_ci* The random() method is implemented in C, executes in a single Python step, 407db96d56Sopenharmony_ci and is, therefore, threadsafe. 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_ci""" 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci# Translated by Guido van Rossum from C source provided by 457db96d56Sopenharmony_ci# Adrian Baddeley. Adapted by Raymond Hettinger for use with 467db96d56Sopenharmony_ci# the Mersenne Twister and os.urandom() core generators. 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_cifrom warnings import warn as _warn 497db96d56Sopenharmony_cifrom math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil 507db96d56Sopenharmony_cifrom math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin 517db96d56Sopenharmony_cifrom math import tau as TWOPI, floor as _floor, isfinite as _isfinite 527db96d56Sopenharmony_cifrom os import urandom as _urandom 537db96d56Sopenharmony_cifrom _collections_abc import Set as _Set, Sequence as _Sequence 547db96d56Sopenharmony_cifrom operator import index as _index 557db96d56Sopenharmony_cifrom itertools import accumulate as _accumulate, repeat as _repeat 567db96d56Sopenharmony_cifrom bisect import bisect as _bisect 577db96d56Sopenharmony_ciimport os as _os 587db96d56Sopenharmony_ciimport _random 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_citry: 617db96d56Sopenharmony_ci # hashlib is pretty heavy to load, try lean internal module first 627db96d56Sopenharmony_ci from _sha512 import sha512 as _sha512 637db96d56Sopenharmony_ciexcept ImportError: 647db96d56Sopenharmony_ci # fallback to official implementation 657db96d56Sopenharmony_ci from hashlib import sha512 as _sha512 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci__all__ = [ 687db96d56Sopenharmony_ci "Random", 697db96d56Sopenharmony_ci "SystemRandom", 707db96d56Sopenharmony_ci "betavariate", 717db96d56Sopenharmony_ci "choice", 727db96d56Sopenharmony_ci "choices", 737db96d56Sopenharmony_ci "expovariate", 747db96d56Sopenharmony_ci "gammavariate", 757db96d56Sopenharmony_ci "gauss", 767db96d56Sopenharmony_ci "getrandbits", 777db96d56Sopenharmony_ci "getstate", 787db96d56Sopenharmony_ci "lognormvariate", 797db96d56Sopenharmony_ci "normalvariate", 807db96d56Sopenharmony_ci "paretovariate", 817db96d56Sopenharmony_ci "randbytes", 827db96d56Sopenharmony_ci "randint", 837db96d56Sopenharmony_ci "random", 847db96d56Sopenharmony_ci "randrange", 857db96d56Sopenharmony_ci "sample", 867db96d56Sopenharmony_ci "seed", 877db96d56Sopenharmony_ci "setstate", 887db96d56Sopenharmony_ci "shuffle", 897db96d56Sopenharmony_ci "triangular", 907db96d56Sopenharmony_ci "uniform", 917db96d56Sopenharmony_ci "vonmisesvariate", 927db96d56Sopenharmony_ci "weibullvariate", 937db96d56Sopenharmony_ci] 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ciNV_MAGICCONST = 4 * _exp(-0.5) / _sqrt(2.0) 967db96d56Sopenharmony_ciLOG4 = _log(4.0) 977db96d56Sopenharmony_ciSG_MAGICCONST = 1.0 + _log(4.5) 987db96d56Sopenharmony_ciBPF = 53 # Number of bits in a float 997db96d56Sopenharmony_ciRECIP_BPF = 2 ** -BPF 1007db96d56Sopenharmony_ci_ONE = 1 1017db96d56Sopenharmony_ci 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ciclass Random(_random.Random): 1047db96d56Sopenharmony_ci """Random number generator base class used by bound module functions. 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci Used to instantiate instances of Random to get generators that don't 1077db96d56Sopenharmony_ci share state. 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci Class Random can also be subclassed if you want to use a different basic 1107db96d56Sopenharmony_ci generator of your own devising: in that case, override the following 1117db96d56Sopenharmony_ci methods: random(), seed(), getstate(), and setstate(). 1127db96d56Sopenharmony_ci Optionally, implement a getrandbits() method so that randrange() 1137db96d56Sopenharmony_ci can cover arbitrarily large ranges. 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci """ 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_ci VERSION = 3 # used by getstate/setstate 1187db96d56Sopenharmony_ci 1197db96d56Sopenharmony_ci def __init__(self, x=None): 1207db96d56Sopenharmony_ci """Initialize an instance. 1217db96d56Sopenharmony_ci 1227db96d56Sopenharmony_ci Optional argument x controls seeding, as for Random.seed(). 1237db96d56Sopenharmony_ci """ 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_ci self.seed(x) 1267db96d56Sopenharmony_ci self.gauss_next = None 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci def seed(self, a=None, version=2): 1297db96d56Sopenharmony_ci """Initialize internal state from a seed. 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci The only supported seed types are None, int, float, 1327db96d56Sopenharmony_ci str, bytes, and bytearray. 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci None or no argument seeds from current time or from an operating 1357db96d56Sopenharmony_ci system specific randomness source if available. 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci If *a* is an int, all bits are used. 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci For version 2 (the default), all of the bits are used if *a* is a str, 1407db96d56Sopenharmony_ci bytes, or bytearray. For version 1 (provided for reproducing random 1417db96d56Sopenharmony_ci sequences from older versions of Python), the algorithm for str and 1427db96d56Sopenharmony_ci bytes generates a narrower range of seeds. 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ci """ 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ci if version == 1 and isinstance(a, (str, bytes)): 1477db96d56Sopenharmony_ci a = a.decode('latin-1') if isinstance(a, bytes) else a 1487db96d56Sopenharmony_ci x = ord(a[0]) << 7 if a else 0 1497db96d56Sopenharmony_ci for c in map(ord, a): 1507db96d56Sopenharmony_ci x = ((1000003 * x) ^ c) & 0xFFFFFFFFFFFFFFFF 1517db96d56Sopenharmony_ci x ^= len(a) 1527db96d56Sopenharmony_ci a = -2 if x == -1 else x 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ci elif version == 2 and isinstance(a, (str, bytes, bytearray)): 1557db96d56Sopenharmony_ci if isinstance(a, str): 1567db96d56Sopenharmony_ci a = a.encode() 1577db96d56Sopenharmony_ci a = int.from_bytes(a + _sha512(a).digest()) 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_ci elif not isinstance(a, (type(None), int, float, str, bytes, bytearray)): 1607db96d56Sopenharmony_ci raise TypeError('The only supported seed types are: None,\n' 1617db96d56Sopenharmony_ci 'int, float, str, bytes, and bytearray.') 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci super().seed(a) 1647db96d56Sopenharmony_ci self.gauss_next = None 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci def getstate(self): 1677db96d56Sopenharmony_ci """Return internal state; can be passed to setstate() later.""" 1687db96d56Sopenharmony_ci return self.VERSION, super().getstate(), self.gauss_next 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci def setstate(self, state): 1717db96d56Sopenharmony_ci """Restore internal state from object returned by getstate().""" 1727db96d56Sopenharmony_ci version = state[0] 1737db96d56Sopenharmony_ci if version == 3: 1747db96d56Sopenharmony_ci version, internalstate, self.gauss_next = state 1757db96d56Sopenharmony_ci super().setstate(internalstate) 1767db96d56Sopenharmony_ci elif version == 2: 1777db96d56Sopenharmony_ci version, internalstate, self.gauss_next = state 1787db96d56Sopenharmony_ci # In version 2, the state was saved as signed ints, which causes 1797db96d56Sopenharmony_ci # inconsistencies between 32/64-bit systems. The state is 1807db96d56Sopenharmony_ci # really unsigned 32-bit ints, so we convert negative ints from 1817db96d56Sopenharmony_ci # version 2 to positive longs for version 3. 1827db96d56Sopenharmony_ci try: 1837db96d56Sopenharmony_ci internalstate = tuple(x % (2 ** 32) for x in internalstate) 1847db96d56Sopenharmony_ci except ValueError as e: 1857db96d56Sopenharmony_ci raise TypeError from e 1867db96d56Sopenharmony_ci super().setstate(internalstate) 1877db96d56Sopenharmony_ci else: 1887db96d56Sopenharmony_ci raise ValueError("state with version %s passed to " 1897db96d56Sopenharmony_ci "Random.setstate() of version %s" % 1907db96d56Sopenharmony_ci (version, self.VERSION)) 1917db96d56Sopenharmony_ci 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ci ## ------------------------------------------------------- 1947db96d56Sopenharmony_ci ## ---- Methods below this point do not need to be overridden or extended 1957db96d56Sopenharmony_ci ## ---- when subclassing for the purpose of using a different core generator. 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_ci 1987db96d56Sopenharmony_ci ## -------------------- pickle support ------------------- 1997db96d56Sopenharmony_ci 2007db96d56Sopenharmony_ci # Issue 17489: Since __reduce__ was defined to fix #759889 this is no 2017db96d56Sopenharmony_ci # longer called; we leave it here because it has been here since random was 2027db96d56Sopenharmony_ci # rewritten back in 2001 and why risk breaking something. 2037db96d56Sopenharmony_ci def __getstate__(self): # for pickle 2047db96d56Sopenharmony_ci return self.getstate() 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci def __setstate__(self, state): # for pickle 2077db96d56Sopenharmony_ci self.setstate(state) 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci def __reduce__(self): 2107db96d56Sopenharmony_ci return self.__class__, (), self.getstate() 2117db96d56Sopenharmony_ci 2127db96d56Sopenharmony_ci 2137db96d56Sopenharmony_ci ## ---- internal support method for evenly distributed integers ---- 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ci def __init_subclass__(cls, /, **kwargs): 2167db96d56Sopenharmony_ci """Control how subclasses generate random integers. 2177db96d56Sopenharmony_ci 2187db96d56Sopenharmony_ci The algorithm a subclass can use depends on the random() and/or 2197db96d56Sopenharmony_ci getrandbits() implementation available to it and determines 2207db96d56Sopenharmony_ci whether it can generate random integers from arbitrarily large 2217db96d56Sopenharmony_ci ranges. 2227db96d56Sopenharmony_ci """ 2237db96d56Sopenharmony_ci 2247db96d56Sopenharmony_ci for c in cls.__mro__: 2257db96d56Sopenharmony_ci if '_randbelow' in c.__dict__: 2267db96d56Sopenharmony_ci # just inherit it 2277db96d56Sopenharmony_ci break 2287db96d56Sopenharmony_ci if 'getrandbits' in c.__dict__: 2297db96d56Sopenharmony_ci cls._randbelow = cls._randbelow_with_getrandbits 2307db96d56Sopenharmony_ci break 2317db96d56Sopenharmony_ci if 'random' in c.__dict__: 2327db96d56Sopenharmony_ci cls._randbelow = cls._randbelow_without_getrandbits 2337db96d56Sopenharmony_ci break 2347db96d56Sopenharmony_ci 2357db96d56Sopenharmony_ci def _randbelow_with_getrandbits(self, n): 2367db96d56Sopenharmony_ci "Return a random int in the range [0,n). Defined for n > 0." 2377db96d56Sopenharmony_ci 2387db96d56Sopenharmony_ci getrandbits = self.getrandbits 2397db96d56Sopenharmony_ci k = n.bit_length() # don't use (n-1) here because n can be 1 2407db96d56Sopenharmony_ci r = getrandbits(k) # 0 <= r < 2**k 2417db96d56Sopenharmony_ci while r >= n: 2427db96d56Sopenharmony_ci r = getrandbits(k) 2437db96d56Sopenharmony_ci return r 2447db96d56Sopenharmony_ci 2457db96d56Sopenharmony_ci def _randbelow_without_getrandbits(self, n, maxsize=1<<BPF): 2467db96d56Sopenharmony_ci """Return a random int in the range [0,n). Defined for n > 0. 2477db96d56Sopenharmony_ci 2487db96d56Sopenharmony_ci The implementation does not use getrandbits, but only random. 2497db96d56Sopenharmony_ci """ 2507db96d56Sopenharmony_ci 2517db96d56Sopenharmony_ci random = self.random 2527db96d56Sopenharmony_ci if n >= maxsize: 2537db96d56Sopenharmony_ci _warn("Underlying random() generator does not supply \n" 2547db96d56Sopenharmony_ci "enough bits to choose from a population range this large.\n" 2557db96d56Sopenharmony_ci "To remove the range limitation, add a getrandbits() method.") 2567db96d56Sopenharmony_ci return _floor(random() * n) 2577db96d56Sopenharmony_ci rem = maxsize % n 2587db96d56Sopenharmony_ci limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0 2597db96d56Sopenharmony_ci r = random() 2607db96d56Sopenharmony_ci while r >= limit: 2617db96d56Sopenharmony_ci r = random() 2627db96d56Sopenharmony_ci return _floor(r * maxsize) % n 2637db96d56Sopenharmony_ci 2647db96d56Sopenharmony_ci _randbelow = _randbelow_with_getrandbits 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_ci ## -------------------------------------------------------- 2687db96d56Sopenharmony_ci ## ---- Methods below this point generate custom distributions 2697db96d56Sopenharmony_ci ## ---- based on the methods defined above. They do not 2707db96d56Sopenharmony_ci ## ---- directly touch the underlying generator and only 2717db96d56Sopenharmony_ci ## ---- access randomness through the methods: random(), 2727db96d56Sopenharmony_ci ## ---- getrandbits(), or _randbelow(). 2737db96d56Sopenharmony_ci 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci ## -------------------- bytes methods --------------------- 2767db96d56Sopenharmony_ci 2777db96d56Sopenharmony_ci def randbytes(self, n): 2787db96d56Sopenharmony_ci """Generate n random bytes.""" 2797db96d56Sopenharmony_ci return self.getrandbits(n * 8).to_bytes(n, 'little') 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci 2827db96d56Sopenharmony_ci ## -------------------- integer methods ------------------- 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci def randrange(self, start, stop=None, step=_ONE): 2857db96d56Sopenharmony_ci """Choose a random item from range(stop) or range(start, stop[, step]). 2867db96d56Sopenharmony_ci 2877db96d56Sopenharmony_ci Roughly equivalent to ``choice(range(start, stop, step))`` but 2887db96d56Sopenharmony_ci supports arbitrarily large ranges and is optimized for common cases. 2897db96d56Sopenharmony_ci 2907db96d56Sopenharmony_ci """ 2917db96d56Sopenharmony_ci 2927db96d56Sopenharmony_ci # This code is a bit messy to make it fast for the 2937db96d56Sopenharmony_ci # common case while still doing adequate error checking. 2947db96d56Sopenharmony_ci try: 2957db96d56Sopenharmony_ci istart = _index(start) 2967db96d56Sopenharmony_ci except TypeError: 2977db96d56Sopenharmony_ci istart = int(start) 2987db96d56Sopenharmony_ci if istart != start: 2997db96d56Sopenharmony_ci _warn('randrange() will raise TypeError in the future', 3007db96d56Sopenharmony_ci DeprecationWarning, 2) 3017db96d56Sopenharmony_ci raise ValueError("non-integer arg 1 for randrange()") 3027db96d56Sopenharmony_ci _warn('non-integer arguments to randrange() have been deprecated ' 3037db96d56Sopenharmony_ci 'since Python 3.10 and will be removed in a subsequent ' 3047db96d56Sopenharmony_ci 'version', 3057db96d56Sopenharmony_ci DeprecationWarning, 2) 3067db96d56Sopenharmony_ci if stop is None: 3077db96d56Sopenharmony_ci # We don't check for "step != 1" because it hasn't been 3087db96d56Sopenharmony_ci # type checked and converted to an integer yet. 3097db96d56Sopenharmony_ci if step is not _ONE: 3107db96d56Sopenharmony_ci raise TypeError('Missing a non-None stop argument') 3117db96d56Sopenharmony_ci if istart > 0: 3127db96d56Sopenharmony_ci return self._randbelow(istart) 3137db96d56Sopenharmony_ci raise ValueError("empty range for randrange()") 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ci # stop argument supplied. 3167db96d56Sopenharmony_ci try: 3177db96d56Sopenharmony_ci istop = _index(stop) 3187db96d56Sopenharmony_ci except TypeError: 3197db96d56Sopenharmony_ci istop = int(stop) 3207db96d56Sopenharmony_ci if istop != stop: 3217db96d56Sopenharmony_ci _warn('randrange() will raise TypeError in the future', 3227db96d56Sopenharmony_ci DeprecationWarning, 2) 3237db96d56Sopenharmony_ci raise ValueError("non-integer stop for randrange()") 3247db96d56Sopenharmony_ci _warn('non-integer arguments to randrange() have been deprecated ' 3257db96d56Sopenharmony_ci 'since Python 3.10 and will be removed in a subsequent ' 3267db96d56Sopenharmony_ci 'version', 3277db96d56Sopenharmony_ci DeprecationWarning, 2) 3287db96d56Sopenharmony_ci width = istop - istart 3297db96d56Sopenharmony_ci try: 3307db96d56Sopenharmony_ci istep = _index(step) 3317db96d56Sopenharmony_ci except TypeError: 3327db96d56Sopenharmony_ci istep = int(step) 3337db96d56Sopenharmony_ci if istep != step: 3347db96d56Sopenharmony_ci _warn('randrange() will raise TypeError in the future', 3357db96d56Sopenharmony_ci DeprecationWarning, 2) 3367db96d56Sopenharmony_ci raise ValueError("non-integer step for randrange()") 3377db96d56Sopenharmony_ci _warn('non-integer arguments to randrange() have been deprecated ' 3387db96d56Sopenharmony_ci 'since Python 3.10 and will be removed in a subsequent ' 3397db96d56Sopenharmony_ci 'version', 3407db96d56Sopenharmony_ci DeprecationWarning, 2) 3417db96d56Sopenharmony_ci # Fast path. 3427db96d56Sopenharmony_ci if istep == 1: 3437db96d56Sopenharmony_ci if width > 0: 3447db96d56Sopenharmony_ci return istart + self._randbelow(width) 3457db96d56Sopenharmony_ci raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci # Non-unit step argument supplied. 3487db96d56Sopenharmony_ci if istep > 0: 3497db96d56Sopenharmony_ci n = (width + istep - 1) // istep 3507db96d56Sopenharmony_ci elif istep < 0: 3517db96d56Sopenharmony_ci n = (width + istep + 1) // istep 3527db96d56Sopenharmony_ci else: 3537db96d56Sopenharmony_ci raise ValueError("zero step for randrange()") 3547db96d56Sopenharmony_ci if n <= 0: 3557db96d56Sopenharmony_ci raise ValueError("empty range for randrange()") 3567db96d56Sopenharmony_ci return istart + istep * self._randbelow(n) 3577db96d56Sopenharmony_ci 3587db96d56Sopenharmony_ci def randint(self, a, b): 3597db96d56Sopenharmony_ci """Return random integer in range [a, b], including both end points. 3607db96d56Sopenharmony_ci """ 3617db96d56Sopenharmony_ci 3627db96d56Sopenharmony_ci return self.randrange(a, b+1) 3637db96d56Sopenharmony_ci 3647db96d56Sopenharmony_ci 3657db96d56Sopenharmony_ci ## -------------------- sequence methods ------------------- 3667db96d56Sopenharmony_ci 3677db96d56Sopenharmony_ci def choice(self, seq): 3687db96d56Sopenharmony_ci """Choose a random element from a non-empty sequence.""" 3697db96d56Sopenharmony_ci 3707db96d56Sopenharmony_ci # As an accommodation for NumPy, we don't use "if not seq" 3717db96d56Sopenharmony_ci # because bool(numpy.array()) raises a ValueError. 3727db96d56Sopenharmony_ci if not len(seq): 3737db96d56Sopenharmony_ci raise IndexError('Cannot choose from an empty sequence') 3747db96d56Sopenharmony_ci return seq[self._randbelow(len(seq))] 3757db96d56Sopenharmony_ci 3767db96d56Sopenharmony_ci def shuffle(self, x): 3777db96d56Sopenharmony_ci """Shuffle list x in place, and return None.""" 3787db96d56Sopenharmony_ci 3797db96d56Sopenharmony_ci randbelow = self._randbelow 3807db96d56Sopenharmony_ci for i in reversed(range(1, len(x))): 3817db96d56Sopenharmony_ci # pick an element in x[:i+1] with which to exchange x[i] 3827db96d56Sopenharmony_ci j = randbelow(i + 1) 3837db96d56Sopenharmony_ci x[i], x[j] = x[j], x[i] 3847db96d56Sopenharmony_ci 3857db96d56Sopenharmony_ci def sample(self, population, k, *, counts=None): 3867db96d56Sopenharmony_ci """Chooses k unique random elements from a population sequence. 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ci Returns a new list containing elements from the population while 3897db96d56Sopenharmony_ci leaving the original population unchanged. The resulting list is 3907db96d56Sopenharmony_ci in selection order so that all sub-slices will also be valid random 3917db96d56Sopenharmony_ci samples. This allows raffle winners (the sample) to be partitioned 3927db96d56Sopenharmony_ci into grand prize and second place winners (the subslices). 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ci Members of the population need not be hashable or unique. If the 3957db96d56Sopenharmony_ci population contains repeats, then each occurrence is a possible 3967db96d56Sopenharmony_ci selection in the sample. 3977db96d56Sopenharmony_ci 3987db96d56Sopenharmony_ci Repeated elements can be specified one at a time or with the optional 3997db96d56Sopenharmony_ci counts parameter. For example: 4007db96d56Sopenharmony_ci 4017db96d56Sopenharmony_ci sample(['red', 'blue'], counts=[4, 2], k=5) 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci is equivalent to: 4047db96d56Sopenharmony_ci 4057db96d56Sopenharmony_ci sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5) 4067db96d56Sopenharmony_ci 4077db96d56Sopenharmony_ci To choose a sample from a range of integers, use range() for the 4087db96d56Sopenharmony_ci population argument. This is especially fast and space efficient 4097db96d56Sopenharmony_ci for sampling from a large population: 4107db96d56Sopenharmony_ci 4117db96d56Sopenharmony_ci sample(range(10000000), 60) 4127db96d56Sopenharmony_ci 4137db96d56Sopenharmony_ci """ 4147db96d56Sopenharmony_ci 4157db96d56Sopenharmony_ci # Sampling without replacement entails tracking either potential 4167db96d56Sopenharmony_ci # selections (the pool) in a list or previous selections in a set. 4177db96d56Sopenharmony_ci 4187db96d56Sopenharmony_ci # When the number of selections is small compared to the 4197db96d56Sopenharmony_ci # population, then tracking selections is efficient, requiring 4207db96d56Sopenharmony_ci # only a small set and an occasional reselection. For 4217db96d56Sopenharmony_ci # a larger number of selections, the pool tracking method is 4227db96d56Sopenharmony_ci # preferred since the list takes less space than the 4237db96d56Sopenharmony_ci # set and it doesn't suffer from frequent reselections. 4247db96d56Sopenharmony_ci 4257db96d56Sopenharmony_ci # The number of calls to _randbelow() is kept at or near k, the 4267db96d56Sopenharmony_ci # theoretical minimum. This is important because running time 4277db96d56Sopenharmony_ci # is dominated by _randbelow() and because it extracts the 4287db96d56Sopenharmony_ci # least entropy from the underlying random number generators. 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci # Memory requirements are kept to the smaller of a k-length 4317db96d56Sopenharmony_ci # set or an n-length list. 4327db96d56Sopenharmony_ci 4337db96d56Sopenharmony_ci # There are other sampling algorithms that do not require 4347db96d56Sopenharmony_ci # auxiliary memory, but they were rejected because they made 4357db96d56Sopenharmony_ci # too many calls to _randbelow(), making them slower and 4367db96d56Sopenharmony_ci # causing them to eat more entropy than necessary. 4377db96d56Sopenharmony_ci 4387db96d56Sopenharmony_ci if not isinstance(population, _Sequence): 4397db96d56Sopenharmony_ci raise TypeError("Population must be a sequence. " 4407db96d56Sopenharmony_ci "For dicts or sets, use sorted(d).") 4417db96d56Sopenharmony_ci n = len(population) 4427db96d56Sopenharmony_ci if counts is not None: 4437db96d56Sopenharmony_ci cum_counts = list(_accumulate(counts)) 4447db96d56Sopenharmony_ci if len(cum_counts) != n: 4457db96d56Sopenharmony_ci raise ValueError('The number of counts does not match the population') 4467db96d56Sopenharmony_ci total = cum_counts.pop() 4477db96d56Sopenharmony_ci if not isinstance(total, int): 4487db96d56Sopenharmony_ci raise TypeError('Counts must be integers') 4497db96d56Sopenharmony_ci if total <= 0: 4507db96d56Sopenharmony_ci raise ValueError('Total of counts must be greater than zero') 4517db96d56Sopenharmony_ci selections = self.sample(range(total), k=k) 4527db96d56Sopenharmony_ci bisect = _bisect 4537db96d56Sopenharmony_ci return [population[bisect(cum_counts, s)] for s in selections] 4547db96d56Sopenharmony_ci randbelow = self._randbelow 4557db96d56Sopenharmony_ci if not 0 <= k <= n: 4567db96d56Sopenharmony_ci raise ValueError("Sample larger than population or is negative") 4577db96d56Sopenharmony_ci result = [None] * k 4587db96d56Sopenharmony_ci setsize = 21 # size of a small set minus size of an empty list 4597db96d56Sopenharmony_ci if k > 5: 4607db96d56Sopenharmony_ci setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets 4617db96d56Sopenharmony_ci if n <= setsize: 4627db96d56Sopenharmony_ci # An n-length list is smaller than a k-length set. 4637db96d56Sopenharmony_ci # Invariant: non-selected at pool[0 : n-i] 4647db96d56Sopenharmony_ci pool = list(population) 4657db96d56Sopenharmony_ci for i in range(k): 4667db96d56Sopenharmony_ci j = randbelow(n - i) 4677db96d56Sopenharmony_ci result[i] = pool[j] 4687db96d56Sopenharmony_ci pool[j] = pool[n - i - 1] # move non-selected item into vacancy 4697db96d56Sopenharmony_ci else: 4707db96d56Sopenharmony_ci selected = set() 4717db96d56Sopenharmony_ci selected_add = selected.add 4727db96d56Sopenharmony_ci for i in range(k): 4737db96d56Sopenharmony_ci j = randbelow(n) 4747db96d56Sopenharmony_ci while j in selected: 4757db96d56Sopenharmony_ci j = randbelow(n) 4767db96d56Sopenharmony_ci selected_add(j) 4777db96d56Sopenharmony_ci result[i] = population[j] 4787db96d56Sopenharmony_ci return result 4797db96d56Sopenharmony_ci 4807db96d56Sopenharmony_ci def choices(self, population, weights=None, *, cum_weights=None, k=1): 4817db96d56Sopenharmony_ci """Return a k sized list of population elements chosen with replacement. 4827db96d56Sopenharmony_ci 4837db96d56Sopenharmony_ci If the relative weights or cumulative weights are not specified, 4847db96d56Sopenharmony_ci the selections are made with equal probability. 4857db96d56Sopenharmony_ci 4867db96d56Sopenharmony_ci """ 4877db96d56Sopenharmony_ci random = self.random 4887db96d56Sopenharmony_ci n = len(population) 4897db96d56Sopenharmony_ci if cum_weights is None: 4907db96d56Sopenharmony_ci if weights is None: 4917db96d56Sopenharmony_ci floor = _floor 4927db96d56Sopenharmony_ci n += 0.0 # convert to float for a small speed improvement 4937db96d56Sopenharmony_ci return [population[floor(random() * n)] for i in _repeat(None, k)] 4947db96d56Sopenharmony_ci try: 4957db96d56Sopenharmony_ci cum_weights = list(_accumulate(weights)) 4967db96d56Sopenharmony_ci except TypeError: 4977db96d56Sopenharmony_ci if not isinstance(weights, int): 4987db96d56Sopenharmony_ci raise 4997db96d56Sopenharmony_ci k = weights 5007db96d56Sopenharmony_ci raise TypeError( 5017db96d56Sopenharmony_ci f'The number of choices must be a keyword argument: {k=}' 5027db96d56Sopenharmony_ci ) from None 5037db96d56Sopenharmony_ci elif weights is not None: 5047db96d56Sopenharmony_ci raise TypeError('Cannot specify both weights and cumulative weights') 5057db96d56Sopenharmony_ci if len(cum_weights) != n: 5067db96d56Sopenharmony_ci raise ValueError('The number of weights does not match the population') 5077db96d56Sopenharmony_ci total = cum_weights[-1] + 0.0 # convert to float 5087db96d56Sopenharmony_ci if total <= 0.0: 5097db96d56Sopenharmony_ci raise ValueError('Total of weights must be greater than zero') 5107db96d56Sopenharmony_ci if not _isfinite(total): 5117db96d56Sopenharmony_ci raise ValueError('Total of weights must be finite') 5127db96d56Sopenharmony_ci bisect = _bisect 5137db96d56Sopenharmony_ci hi = n - 1 5147db96d56Sopenharmony_ci return [population[bisect(cum_weights, random() * total, 0, hi)] 5157db96d56Sopenharmony_ci for i in _repeat(None, k)] 5167db96d56Sopenharmony_ci 5177db96d56Sopenharmony_ci 5187db96d56Sopenharmony_ci ## -------------------- real-valued distributions ------------------- 5197db96d56Sopenharmony_ci 5207db96d56Sopenharmony_ci def uniform(self, a, b): 5217db96d56Sopenharmony_ci "Get a random number in the range [a, b) or [a, b] depending on rounding." 5227db96d56Sopenharmony_ci return a + (b - a) * self.random() 5237db96d56Sopenharmony_ci 5247db96d56Sopenharmony_ci def triangular(self, low=0.0, high=1.0, mode=None): 5257db96d56Sopenharmony_ci """Triangular distribution. 5267db96d56Sopenharmony_ci 5277db96d56Sopenharmony_ci Continuous distribution bounded by given lower and upper limits, 5287db96d56Sopenharmony_ci and having a given mode value in-between. 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci http://en.wikipedia.org/wiki/Triangular_distribution 5317db96d56Sopenharmony_ci 5327db96d56Sopenharmony_ci """ 5337db96d56Sopenharmony_ci u = self.random() 5347db96d56Sopenharmony_ci try: 5357db96d56Sopenharmony_ci c = 0.5 if mode is None else (mode - low) / (high - low) 5367db96d56Sopenharmony_ci except ZeroDivisionError: 5377db96d56Sopenharmony_ci return low 5387db96d56Sopenharmony_ci if u > c: 5397db96d56Sopenharmony_ci u = 1.0 - u 5407db96d56Sopenharmony_ci c = 1.0 - c 5417db96d56Sopenharmony_ci low, high = high, low 5427db96d56Sopenharmony_ci return low + (high - low) * _sqrt(u * c) 5437db96d56Sopenharmony_ci 5447db96d56Sopenharmony_ci def normalvariate(self, mu=0.0, sigma=1.0): 5457db96d56Sopenharmony_ci """Normal distribution. 5467db96d56Sopenharmony_ci 5477db96d56Sopenharmony_ci mu is the mean, and sigma is the standard deviation. 5487db96d56Sopenharmony_ci 5497db96d56Sopenharmony_ci """ 5507db96d56Sopenharmony_ci # Uses Kinderman and Monahan method. Reference: Kinderman, 5517db96d56Sopenharmony_ci # A.J. and Monahan, J.F., "Computer generation of random 5527db96d56Sopenharmony_ci # variables using the ratio of uniform deviates", ACM Trans 5537db96d56Sopenharmony_ci # Math Software, 3, (1977), pp257-260. 5547db96d56Sopenharmony_ci 5557db96d56Sopenharmony_ci random = self.random 5567db96d56Sopenharmony_ci while True: 5577db96d56Sopenharmony_ci u1 = random() 5587db96d56Sopenharmony_ci u2 = 1.0 - random() 5597db96d56Sopenharmony_ci z = NV_MAGICCONST * (u1 - 0.5) / u2 5607db96d56Sopenharmony_ci zz = z * z / 4.0 5617db96d56Sopenharmony_ci if zz <= -_log(u2): 5627db96d56Sopenharmony_ci break 5637db96d56Sopenharmony_ci return mu + z * sigma 5647db96d56Sopenharmony_ci 5657db96d56Sopenharmony_ci def gauss(self, mu=0.0, sigma=1.0): 5667db96d56Sopenharmony_ci """Gaussian distribution. 5677db96d56Sopenharmony_ci 5687db96d56Sopenharmony_ci mu is the mean, and sigma is the standard deviation. This is 5697db96d56Sopenharmony_ci slightly faster than the normalvariate() function. 5707db96d56Sopenharmony_ci 5717db96d56Sopenharmony_ci Not thread-safe without a lock around calls. 5727db96d56Sopenharmony_ci 5737db96d56Sopenharmony_ci """ 5747db96d56Sopenharmony_ci # When x and y are two variables from [0, 1), uniformly 5757db96d56Sopenharmony_ci # distributed, then 5767db96d56Sopenharmony_ci # 5777db96d56Sopenharmony_ci # cos(2*pi*x)*sqrt(-2*log(1-y)) 5787db96d56Sopenharmony_ci # sin(2*pi*x)*sqrt(-2*log(1-y)) 5797db96d56Sopenharmony_ci # 5807db96d56Sopenharmony_ci # are two *independent* variables with normal distribution 5817db96d56Sopenharmony_ci # (mu = 0, sigma = 1). 5827db96d56Sopenharmony_ci # (Lambert Meertens) 5837db96d56Sopenharmony_ci # (corrected version; bug discovered by Mike Miller, fixed by LM) 5847db96d56Sopenharmony_ci 5857db96d56Sopenharmony_ci # Multithreading note: When two threads call this function 5867db96d56Sopenharmony_ci # simultaneously, it is possible that they will receive the 5877db96d56Sopenharmony_ci # same return value. The window is very small though. To 5887db96d56Sopenharmony_ci # avoid this, you have to use a lock around all calls. (I 5897db96d56Sopenharmony_ci # didn't want to slow this down in the serial case by using a 5907db96d56Sopenharmony_ci # lock here.) 5917db96d56Sopenharmony_ci 5927db96d56Sopenharmony_ci random = self.random 5937db96d56Sopenharmony_ci z = self.gauss_next 5947db96d56Sopenharmony_ci self.gauss_next = None 5957db96d56Sopenharmony_ci if z is None: 5967db96d56Sopenharmony_ci x2pi = random() * TWOPI 5977db96d56Sopenharmony_ci g2rad = _sqrt(-2.0 * _log(1.0 - random())) 5987db96d56Sopenharmony_ci z = _cos(x2pi) * g2rad 5997db96d56Sopenharmony_ci self.gauss_next = _sin(x2pi) * g2rad 6007db96d56Sopenharmony_ci 6017db96d56Sopenharmony_ci return mu + z * sigma 6027db96d56Sopenharmony_ci 6037db96d56Sopenharmony_ci def lognormvariate(self, mu, sigma): 6047db96d56Sopenharmony_ci """Log normal distribution. 6057db96d56Sopenharmony_ci 6067db96d56Sopenharmony_ci If you take the natural logarithm of this distribution, you'll get a 6077db96d56Sopenharmony_ci normal distribution with mean mu and standard deviation sigma. 6087db96d56Sopenharmony_ci mu can have any value, and sigma must be greater than zero. 6097db96d56Sopenharmony_ci 6107db96d56Sopenharmony_ci """ 6117db96d56Sopenharmony_ci return _exp(self.normalvariate(mu, sigma)) 6127db96d56Sopenharmony_ci 6137db96d56Sopenharmony_ci def expovariate(self, lambd): 6147db96d56Sopenharmony_ci """Exponential distribution. 6157db96d56Sopenharmony_ci 6167db96d56Sopenharmony_ci lambd is 1.0 divided by the desired mean. It should be 6177db96d56Sopenharmony_ci nonzero. (The parameter would be called "lambda", but that is 6187db96d56Sopenharmony_ci a reserved word in Python.) Returned values range from 0 to 6197db96d56Sopenharmony_ci positive infinity if lambd is positive, and from negative 6207db96d56Sopenharmony_ci infinity to 0 if lambd is negative. 6217db96d56Sopenharmony_ci 6227db96d56Sopenharmony_ci """ 6237db96d56Sopenharmony_ci # lambd: rate lambd = 1/mean 6247db96d56Sopenharmony_ci # ('lambda' is a Python reserved word) 6257db96d56Sopenharmony_ci 6267db96d56Sopenharmony_ci # we use 1-random() instead of random() to preclude the 6277db96d56Sopenharmony_ci # possibility of taking the log of zero. 6287db96d56Sopenharmony_ci return -_log(1.0 - self.random()) / lambd 6297db96d56Sopenharmony_ci 6307db96d56Sopenharmony_ci def vonmisesvariate(self, mu, kappa): 6317db96d56Sopenharmony_ci """Circular data distribution. 6327db96d56Sopenharmony_ci 6337db96d56Sopenharmony_ci mu is the mean angle, expressed in radians between 0 and 2*pi, and 6347db96d56Sopenharmony_ci kappa is the concentration parameter, which must be greater than or 6357db96d56Sopenharmony_ci equal to zero. If kappa is equal to zero, this distribution reduces 6367db96d56Sopenharmony_ci to a uniform random angle over the range 0 to 2*pi. 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ci """ 6397db96d56Sopenharmony_ci # Based upon an algorithm published in: Fisher, N.I., 6407db96d56Sopenharmony_ci # "Statistical Analysis of Circular Data", Cambridge 6417db96d56Sopenharmony_ci # University Press, 1993. 6427db96d56Sopenharmony_ci 6437db96d56Sopenharmony_ci # Thanks to Magnus Kessler for a correction to the 6447db96d56Sopenharmony_ci # implementation of step 4. 6457db96d56Sopenharmony_ci 6467db96d56Sopenharmony_ci random = self.random 6477db96d56Sopenharmony_ci if kappa <= 1e-6: 6487db96d56Sopenharmony_ci return TWOPI * random() 6497db96d56Sopenharmony_ci 6507db96d56Sopenharmony_ci s = 0.5 / kappa 6517db96d56Sopenharmony_ci r = s + _sqrt(1.0 + s * s) 6527db96d56Sopenharmony_ci 6537db96d56Sopenharmony_ci while True: 6547db96d56Sopenharmony_ci u1 = random() 6557db96d56Sopenharmony_ci z = _cos(_pi * u1) 6567db96d56Sopenharmony_ci 6577db96d56Sopenharmony_ci d = z / (r + z) 6587db96d56Sopenharmony_ci u2 = random() 6597db96d56Sopenharmony_ci if u2 < 1.0 - d * d or u2 <= (1.0 - d) * _exp(d): 6607db96d56Sopenharmony_ci break 6617db96d56Sopenharmony_ci 6627db96d56Sopenharmony_ci q = 1.0 / r 6637db96d56Sopenharmony_ci f = (q + z) / (1.0 + q * z) 6647db96d56Sopenharmony_ci u3 = random() 6657db96d56Sopenharmony_ci if u3 > 0.5: 6667db96d56Sopenharmony_ci theta = (mu + _acos(f)) % TWOPI 6677db96d56Sopenharmony_ci else: 6687db96d56Sopenharmony_ci theta = (mu - _acos(f)) % TWOPI 6697db96d56Sopenharmony_ci 6707db96d56Sopenharmony_ci return theta 6717db96d56Sopenharmony_ci 6727db96d56Sopenharmony_ci def gammavariate(self, alpha, beta): 6737db96d56Sopenharmony_ci """Gamma distribution. Not the gamma function! 6747db96d56Sopenharmony_ci 6757db96d56Sopenharmony_ci Conditions on the parameters are alpha > 0 and beta > 0. 6767db96d56Sopenharmony_ci 6777db96d56Sopenharmony_ci The probability distribution function is: 6787db96d56Sopenharmony_ci 6797db96d56Sopenharmony_ci x ** (alpha - 1) * math.exp(-x / beta) 6807db96d56Sopenharmony_ci pdf(x) = -------------------------------------- 6817db96d56Sopenharmony_ci math.gamma(alpha) * beta ** alpha 6827db96d56Sopenharmony_ci 6837db96d56Sopenharmony_ci """ 6847db96d56Sopenharmony_ci # alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2 6857db96d56Sopenharmony_ci 6867db96d56Sopenharmony_ci # Warning: a few older sources define the gamma distribution in terms 6877db96d56Sopenharmony_ci # of alpha > -1.0 6887db96d56Sopenharmony_ci if alpha <= 0.0 or beta <= 0.0: 6897db96d56Sopenharmony_ci raise ValueError('gammavariate: alpha and beta must be > 0.0') 6907db96d56Sopenharmony_ci 6917db96d56Sopenharmony_ci random = self.random 6927db96d56Sopenharmony_ci if alpha > 1.0: 6937db96d56Sopenharmony_ci 6947db96d56Sopenharmony_ci # Uses R.C.H. Cheng, "The generation of Gamma 6957db96d56Sopenharmony_ci # variables with non-integral shape parameters", 6967db96d56Sopenharmony_ci # Applied Statistics, (1977), 26, No. 1, p71-74 6977db96d56Sopenharmony_ci 6987db96d56Sopenharmony_ci ainv = _sqrt(2.0 * alpha - 1.0) 6997db96d56Sopenharmony_ci bbb = alpha - LOG4 7007db96d56Sopenharmony_ci ccc = alpha + ainv 7017db96d56Sopenharmony_ci 7027db96d56Sopenharmony_ci while True: 7037db96d56Sopenharmony_ci u1 = random() 7047db96d56Sopenharmony_ci if not 1e-7 < u1 < 0.9999999: 7057db96d56Sopenharmony_ci continue 7067db96d56Sopenharmony_ci u2 = 1.0 - random() 7077db96d56Sopenharmony_ci v = _log(u1 / (1.0 - u1)) / ainv 7087db96d56Sopenharmony_ci x = alpha * _exp(v) 7097db96d56Sopenharmony_ci z = u1 * u1 * u2 7107db96d56Sopenharmony_ci r = bbb + ccc * v - x 7117db96d56Sopenharmony_ci if r + SG_MAGICCONST - 4.5 * z >= 0.0 or r >= _log(z): 7127db96d56Sopenharmony_ci return x * beta 7137db96d56Sopenharmony_ci 7147db96d56Sopenharmony_ci elif alpha == 1.0: 7157db96d56Sopenharmony_ci # expovariate(1/beta) 7167db96d56Sopenharmony_ci return -_log(1.0 - random()) * beta 7177db96d56Sopenharmony_ci 7187db96d56Sopenharmony_ci else: 7197db96d56Sopenharmony_ci # alpha is between 0 and 1 (exclusive) 7207db96d56Sopenharmony_ci # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle 7217db96d56Sopenharmony_ci while True: 7227db96d56Sopenharmony_ci u = random() 7237db96d56Sopenharmony_ci b = (_e + alpha) / _e 7247db96d56Sopenharmony_ci p = b * u 7257db96d56Sopenharmony_ci if p <= 1.0: 7267db96d56Sopenharmony_ci x = p ** (1.0 / alpha) 7277db96d56Sopenharmony_ci else: 7287db96d56Sopenharmony_ci x = -_log((b - p) / alpha) 7297db96d56Sopenharmony_ci u1 = random() 7307db96d56Sopenharmony_ci if p > 1.0: 7317db96d56Sopenharmony_ci if u1 <= x ** (alpha - 1.0): 7327db96d56Sopenharmony_ci break 7337db96d56Sopenharmony_ci elif u1 <= _exp(-x): 7347db96d56Sopenharmony_ci break 7357db96d56Sopenharmony_ci return x * beta 7367db96d56Sopenharmony_ci 7377db96d56Sopenharmony_ci def betavariate(self, alpha, beta): 7387db96d56Sopenharmony_ci """Beta distribution. 7397db96d56Sopenharmony_ci 7407db96d56Sopenharmony_ci Conditions on the parameters are alpha > 0 and beta > 0. 7417db96d56Sopenharmony_ci Returned values range between 0 and 1. 7427db96d56Sopenharmony_ci 7437db96d56Sopenharmony_ci """ 7447db96d56Sopenharmony_ci ## See 7457db96d56Sopenharmony_ci ## http://mail.python.org/pipermail/python-bugs-list/2001-January/003752.html 7467db96d56Sopenharmony_ci ## for Ivan Frohne's insightful analysis of why the original implementation: 7477db96d56Sopenharmony_ci ## 7487db96d56Sopenharmony_ci ## def betavariate(self, alpha, beta): 7497db96d56Sopenharmony_ci ## # Discrete Event Simulation in C, pp 87-88. 7507db96d56Sopenharmony_ci ## 7517db96d56Sopenharmony_ci ## y = self.expovariate(alpha) 7527db96d56Sopenharmony_ci ## z = self.expovariate(1.0/beta) 7537db96d56Sopenharmony_ci ## return z/(y+z) 7547db96d56Sopenharmony_ci ## 7557db96d56Sopenharmony_ci ## was dead wrong, and how it probably got that way. 7567db96d56Sopenharmony_ci 7577db96d56Sopenharmony_ci # This version due to Janne Sinkkonen, and matches all the std 7587db96d56Sopenharmony_ci # texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution"). 7597db96d56Sopenharmony_ci y = self.gammavariate(alpha, 1.0) 7607db96d56Sopenharmony_ci if y: 7617db96d56Sopenharmony_ci return y / (y + self.gammavariate(beta, 1.0)) 7627db96d56Sopenharmony_ci return 0.0 7637db96d56Sopenharmony_ci 7647db96d56Sopenharmony_ci def paretovariate(self, alpha): 7657db96d56Sopenharmony_ci """Pareto distribution. alpha is the shape parameter.""" 7667db96d56Sopenharmony_ci # Jain, pg. 495 7677db96d56Sopenharmony_ci 7687db96d56Sopenharmony_ci u = 1.0 - self.random() 7697db96d56Sopenharmony_ci return u ** (-1.0 / alpha) 7707db96d56Sopenharmony_ci 7717db96d56Sopenharmony_ci def weibullvariate(self, alpha, beta): 7727db96d56Sopenharmony_ci """Weibull distribution. 7737db96d56Sopenharmony_ci 7747db96d56Sopenharmony_ci alpha is the scale parameter and beta is the shape parameter. 7757db96d56Sopenharmony_ci 7767db96d56Sopenharmony_ci """ 7777db96d56Sopenharmony_ci # Jain, pg. 499; bug fix courtesy Bill Arms 7787db96d56Sopenharmony_ci 7797db96d56Sopenharmony_ci u = 1.0 - self.random() 7807db96d56Sopenharmony_ci return alpha * (-_log(u)) ** (1.0 / beta) 7817db96d56Sopenharmony_ci 7827db96d56Sopenharmony_ci 7837db96d56Sopenharmony_ci## ------------------------------------------------------------------ 7847db96d56Sopenharmony_ci## --------------- Operating System Random Source ------------------ 7857db96d56Sopenharmony_ci 7867db96d56Sopenharmony_ci 7877db96d56Sopenharmony_ciclass SystemRandom(Random): 7887db96d56Sopenharmony_ci """Alternate random number generator using sources provided 7897db96d56Sopenharmony_ci by the operating system (such as /dev/urandom on Unix or 7907db96d56Sopenharmony_ci CryptGenRandom on Windows). 7917db96d56Sopenharmony_ci 7927db96d56Sopenharmony_ci Not available on all systems (see os.urandom() for details). 7937db96d56Sopenharmony_ci 7947db96d56Sopenharmony_ci """ 7957db96d56Sopenharmony_ci 7967db96d56Sopenharmony_ci def random(self): 7977db96d56Sopenharmony_ci """Get the next random number in the range 0.0 <= X < 1.0.""" 7987db96d56Sopenharmony_ci return (int.from_bytes(_urandom(7)) >> 3) * RECIP_BPF 7997db96d56Sopenharmony_ci 8007db96d56Sopenharmony_ci def getrandbits(self, k): 8017db96d56Sopenharmony_ci """getrandbits(k) -> x. Generates an int with k random bits.""" 8027db96d56Sopenharmony_ci if k < 0: 8037db96d56Sopenharmony_ci raise ValueError('number of bits must be non-negative') 8047db96d56Sopenharmony_ci numbytes = (k + 7) // 8 # bits / 8 and rounded up 8057db96d56Sopenharmony_ci x = int.from_bytes(_urandom(numbytes)) 8067db96d56Sopenharmony_ci return x >> (numbytes * 8 - k) # trim excess bits 8077db96d56Sopenharmony_ci 8087db96d56Sopenharmony_ci def randbytes(self, n): 8097db96d56Sopenharmony_ci """Generate n random bytes.""" 8107db96d56Sopenharmony_ci # os.urandom(n) fails with ValueError for n < 0 8117db96d56Sopenharmony_ci # and returns an empty bytes string for n == 0. 8127db96d56Sopenharmony_ci return _urandom(n) 8137db96d56Sopenharmony_ci 8147db96d56Sopenharmony_ci def seed(self, *args, **kwds): 8157db96d56Sopenharmony_ci "Stub method. Not used for a system random number generator." 8167db96d56Sopenharmony_ci return None 8177db96d56Sopenharmony_ci 8187db96d56Sopenharmony_ci def _notimplemented(self, *args, **kwds): 8197db96d56Sopenharmony_ci "Method should not be called for a system random number generator." 8207db96d56Sopenharmony_ci raise NotImplementedError('System entropy source does not have state.') 8217db96d56Sopenharmony_ci getstate = setstate = _notimplemented 8227db96d56Sopenharmony_ci 8237db96d56Sopenharmony_ci 8247db96d56Sopenharmony_ci# ---------------------------------------------------------------------- 8257db96d56Sopenharmony_ci# Create one instance, seeded from current time, and export its methods 8267db96d56Sopenharmony_ci# as module-level functions. The functions share state across all uses 8277db96d56Sopenharmony_ci# (both in the user's code and in the Python libraries), but that's fine 8287db96d56Sopenharmony_ci# for most programs and is easier for the casual user than making them 8297db96d56Sopenharmony_ci# instantiate their own Random() instance. 8307db96d56Sopenharmony_ci 8317db96d56Sopenharmony_ci_inst = Random() 8327db96d56Sopenharmony_ciseed = _inst.seed 8337db96d56Sopenharmony_cirandom = _inst.random 8347db96d56Sopenharmony_ciuniform = _inst.uniform 8357db96d56Sopenharmony_citriangular = _inst.triangular 8367db96d56Sopenharmony_cirandint = _inst.randint 8377db96d56Sopenharmony_cichoice = _inst.choice 8387db96d56Sopenharmony_cirandrange = _inst.randrange 8397db96d56Sopenharmony_cisample = _inst.sample 8407db96d56Sopenharmony_cishuffle = _inst.shuffle 8417db96d56Sopenharmony_cichoices = _inst.choices 8427db96d56Sopenharmony_cinormalvariate = _inst.normalvariate 8437db96d56Sopenharmony_cilognormvariate = _inst.lognormvariate 8447db96d56Sopenharmony_ciexpovariate = _inst.expovariate 8457db96d56Sopenharmony_civonmisesvariate = _inst.vonmisesvariate 8467db96d56Sopenharmony_cigammavariate = _inst.gammavariate 8477db96d56Sopenharmony_cigauss = _inst.gauss 8487db96d56Sopenharmony_cibetavariate = _inst.betavariate 8497db96d56Sopenharmony_ciparetovariate = _inst.paretovariate 8507db96d56Sopenharmony_ciweibullvariate = _inst.weibullvariate 8517db96d56Sopenharmony_cigetstate = _inst.getstate 8527db96d56Sopenharmony_cisetstate = _inst.setstate 8537db96d56Sopenharmony_cigetrandbits = _inst.getrandbits 8547db96d56Sopenharmony_cirandbytes = _inst.randbytes 8557db96d56Sopenharmony_ci 8567db96d56Sopenharmony_ci 8577db96d56Sopenharmony_ci## ------------------------------------------------------ 8587db96d56Sopenharmony_ci## ----------------- test program ----------------------- 8597db96d56Sopenharmony_ci 8607db96d56Sopenharmony_cidef _test_generator(n, func, args): 8617db96d56Sopenharmony_ci from statistics import stdev, fmean as mean 8627db96d56Sopenharmony_ci from time import perf_counter 8637db96d56Sopenharmony_ci 8647db96d56Sopenharmony_ci t0 = perf_counter() 8657db96d56Sopenharmony_ci data = [func(*args) for i in _repeat(None, n)] 8667db96d56Sopenharmony_ci t1 = perf_counter() 8677db96d56Sopenharmony_ci 8687db96d56Sopenharmony_ci xbar = mean(data) 8697db96d56Sopenharmony_ci sigma = stdev(data, xbar) 8707db96d56Sopenharmony_ci low = min(data) 8717db96d56Sopenharmony_ci high = max(data) 8727db96d56Sopenharmony_ci 8737db96d56Sopenharmony_ci print(f'{t1 - t0:.3f} sec, {n} times {func.__name__}') 8747db96d56Sopenharmony_ci print('avg %g, stddev %g, min %g, max %g\n' % (xbar, sigma, low, high)) 8757db96d56Sopenharmony_ci 8767db96d56Sopenharmony_ci 8777db96d56Sopenharmony_cidef _test(N=2000): 8787db96d56Sopenharmony_ci _test_generator(N, random, ()) 8797db96d56Sopenharmony_ci _test_generator(N, normalvariate, (0.0, 1.0)) 8807db96d56Sopenharmony_ci _test_generator(N, lognormvariate, (0.0, 1.0)) 8817db96d56Sopenharmony_ci _test_generator(N, vonmisesvariate, (0.0, 1.0)) 8827db96d56Sopenharmony_ci _test_generator(N, gammavariate, (0.01, 1.0)) 8837db96d56Sopenharmony_ci _test_generator(N, gammavariate, (0.1, 1.0)) 8847db96d56Sopenharmony_ci _test_generator(N, gammavariate, (0.1, 2.0)) 8857db96d56Sopenharmony_ci _test_generator(N, gammavariate, (0.5, 1.0)) 8867db96d56Sopenharmony_ci _test_generator(N, gammavariate, (0.9, 1.0)) 8877db96d56Sopenharmony_ci _test_generator(N, gammavariate, (1.0, 1.0)) 8887db96d56Sopenharmony_ci _test_generator(N, gammavariate, (2.0, 1.0)) 8897db96d56Sopenharmony_ci _test_generator(N, gammavariate, (20.0, 1.0)) 8907db96d56Sopenharmony_ci _test_generator(N, gammavariate, (200.0, 1.0)) 8917db96d56Sopenharmony_ci _test_generator(N, gauss, (0.0, 1.0)) 8927db96d56Sopenharmony_ci _test_generator(N, betavariate, (3.0, 3.0)) 8937db96d56Sopenharmony_ci _test_generator(N, triangular, (0.0, 1.0, 1.0 / 3.0)) 8947db96d56Sopenharmony_ci 8957db96d56Sopenharmony_ci 8967db96d56Sopenharmony_ci## ------------------------------------------------------ 8977db96d56Sopenharmony_ci## ------------------ fork support --------------------- 8987db96d56Sopenharmony_ci 8997db96d56Sopenharmony_ciif hasattr(_os, "fork"): 9007db96d56Sopenharmony_ci _os.register_at_fork(after_in_child=_inst.seed) 9017db96d56Sopenharmony_ci 9027db96d56Sopenharmony_ci 9037db96d56Sopenharmony_ciif __name__ == '__main__': 9047db96d56Sopenharmony_ci _test() 905