diff options
author | Raymond Hettinger <python@rcn.com> | 2008-01-13 23:40:30 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2008-01-13 23:40:30 (GMT) |
commit | 28de64fd0fe45475e6d2263eec25c3d19c00074b (patch) | |
tree | d34ee792cd1e4cdc25cfc8bf3304b2ffd7521e80 | |
parent | f7ec7a81a557947fff5cb02510e121e8219e7811 (diff) | |
download | cpython-28de64fd0fe45475e6d2263eec25c3d19c00074b.zip cpython-28de64fd0fe45475e6d2263eec25c3d19c00074b.tar.gz cpython-28de64fd0fe45475e6d2263eec25c3d19c00074b.tar.bz2 |
Remove defunct parts of the random module
-rw-r--r-- | Doc/library/random.rst | 46 | ||||
-rw-r--r-- | Lib/random.py | 166 | ||||
-rw-r--r-- | Lib/test/test_generators.py | 44 | ||||
-rw-r--r-- | Lib/test/test_random.py | 64 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_randommodule.c | 69 |
6 files changed, 32 insertions, 360 deletions
diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 18c063c..ff5fb77 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -28,25 +28,14 @@ for cryptographic purposes. The functions supplied by this module are actually bound methods of a hidden instance of the :class:`random.Random` class. You can instantiate your own -instances of :class:`Random` to get generators that don't share state. This is -especially useful for multi-threaded programs, creating a different instance of -:class:`Random` for each thread, and using the :meth:`jumpahead` method to make -it likely that the generated sequences seen by each thread don't overlap. +instances of :class:`Random` to get generators that don't share state. Class :class:`Random` can also be subclassed if you want to use a different basic generator of your own devising: in that case, override the :meth:`random`, -:meth:`seed`, :meth:`getstate`, :meth:`setstate` and :meth:`jumpahead` methods. +:meth:`seed`, :meth:`getstate`, and :meth:`setstate`. Optionally, a new generator can supply a :meth:`getrandombits` method --- this allows :meth:`randrange` to produce selections over an arbitrarily large range. -As an example of subclassing, the :mod:`random` module provides the -:class:`WichmannHill` class that implements an alternative generator in pure -Python. The class provides a backward compatible way to reproduce results from -earlier versions of Python, which used the Wichmann-Hill algorithm as the core -generator. Note that this Wichmann-Hill generator can no longer be recommended: -its period is too short by contemporary standards, and the sequence generated is -known to fail some stringent randomness tests. See the references below for a -recent variant that repairs these flaws. Bookkeeping functions: @@ -79,17 +68,6 @@ Bookkeeping functions: the time :func:`setstate` was called. -.. function:: jumpahead(n) - - Change the internal state to one different from and likely far away from the - current state. *n* is a non-negative integer which is used to scramble the - current state vector. This is most useful in multi-threaded programs, in - conjuction with multiple instances of the :class:`Random` class: - :meth:`setstate` or :meth:`seed` can be used to force all instances into the - same internal state, and then :meth:`jumpahead` can be used to force the - instances' states far apart. - - .. function:: getrandbits(k) Returns a python integer with *k* random bits. This method is supplied with @@ -224,24 +202,6 @@ be found in any statistics text. Alternative Generators: -.. class:: WichmannHill([seed]) - - Class that implements the Wichmann-Hill algorithm as the core generator. Has all - of the same methods as :class:`Random` plus the :meth:`whseed` method described - below. Because this class is implemented in pure Python, it is not threadsafe - and may require locks between calls. The period of the generator is - 6,953,607,871,644 which is small enough to require care that two independent - random sequences do not overlap. - - -.. function:: whseed([x]) - - This is obsolete, supplied for bit-level compatibility with versions of Python - prior to 2.1. See :func:`seed` for details. :func:`whseed` does not guarantee - that distinct integer arguments yield distinct internal states, and can yield no - more than about 2\*\*24 distinct internal states in all. - - .. class:: SystemRandom([seed]) Class that uses the :func:`os.urandom` function for generating random numbers @@ -281,6 +241,4 @@ Examples of basic usage:: equidistributed uniform pseudorandom number generator", ACM Transactions on Modeling and Computer Simulation Vol. 8, No. 1, January pp.3-30 1998. - Wichmann, B. A. & Hill, I. D., "Algorithm AS 183: An efficient and portable - pseudo-random number generator", Applied Statistics 31 (1982) 188-190. diff --git a/Lib/random.py b/Lib/random.py index d8d2c1e..5e57203 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -30,9 +30,6 @@ General notes on the underlying Mersenne Twister core generator: * The period is 2**19937-1. * It is one of the most extensively tested generators in existence. -* Without a direct way to compute N steps forward, the semantics of - jumpahead(n) are weakened to simply jump to another distant state and rely - on the large period to avoid overlapping sequences. * The random() method is implemented in C, executes in a single Python step, and is, therefore, threadsafe. @@ -49,7 +46,7 @@ __all__ = ["Random","seed","random","uniform","randint","choice","sample", "randrange","shuffle","normalvariate","lognormvariate", "expovariate","vonmisesvariate","gammavariate", "gauss","betavariate","paretovariate","weibullvariate", - "getstate","setstate","jumpahead", "WichmannHill", "getrandbits", + "getstate","setstate", "getrandbits", "SystemRandom"] NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) @@ -70,14 +67,11 @@ class Random(_random.Random): """Random number generator base class used by bound module functions. Used to instantiate instances of Random to get generators that don't - share state. Especially useful for multi-threaded programs, creating - a different instance of Random for each thread, and using the jumpahead() - method to ensure that the generated sequences seen by each thread don't - overlap. + share state. Class Random can also be subclassed if you want to use a different basic generator of your own devising: in that case, override the following - methods: random(), seed(), getstate(), setstate() and jumpahead(). + methods: random(), seed(), getstate(), and setstate(). Optionally, implement a getrandombits() method so that randrange() can cover arbitrarily large ranges. @@ -615,156 +609,6 @@ class Random(_random.Random): u = 1.0 - self.random() return alpha * pow(-_log(u), 1.0/beta) -## -------------------- Wichmann-Hill ------------------- - -class WichmannHill(Random): - - VERSION = 1 # used by getstate/setstate - - def seed(self, a=None): - """Initialize internal state from hashable object. - - None or no argument seeds from current time or from an operating - system specific randomness source if available. - - If a is not None or an int or long, hash(a) is used instead. - - If a is an int or long, a is used directly. Distinct values between - 0 and 27814431486575L inclusive are guaranteed to yield distinct - internal states (this guarantee is specific to the default - Wichmann-Hill generator). - """ - - if a is None: - try: - a = int(_hexlify(_urandom(16)), 16) - except NotImplementedError: - import time - a = int(time.time() * 256) # use fractional seconds - - if not isinstance(a, int): - a = hash(a) - - a, x = divmod(a, 30268) - a, y = divmod(a, 30306) - a, z = divmod(a, 30322) - self._seed = int(x)+1, int(y)+1, int(z)+1 - - self.gauss_next = None - - 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 getstate(self): - """Return internal state; can be passed to setstate() later.""" - return self.VERSION, self._seed, self.gauss_next - - 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 jumpahead(self, n): - """Act as if n calls to random() were made, but quickly. - - n is an int, greater than or equal to 0. - - Example use: If you have 2 threads and know that each will - consume no more than a million random numbers, create two Random - objects r1 and r2, then do - r2.setstate(r1.getstate()) - r2.jumpahead(1000000) - Then r1 and r2 will use guaranteed-disjoint segments of the full - period. - """ - - if not n >= 0: - raise ValueError("n must be >= 0") - x, y, z = self._seed - x = int(x * pow(171, n, 30269)) % 30269 - y = int(y * pow(172, n, 30307)) % 30307 - z = int(z * pow(170, n, 30323)) % 30323 - self._seed = x, y, z - - 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) == int: - 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 = int(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) - - self.gauss_next = None - - def whseed(self, a=None): - """Seed from hashable object's hash code. - - None or no argument seeds from current time. It is not guaranteed - that objects with distinct hash codes lead to distinct internal - states. - - This is obsolete, provided for compatibility with the seed routine - used prior to Python 2.1. Use the .seed() method instead. - """ - - if a is None: - self.__whseed() - return - 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) - ## --------------- Operating System Random Source ------------------ class SystemRandom(Random): @@ -789,10 +633,9 @@ class SystemRandom(Random): x = int(_hexlify(_urandom(bytes)), 16) return x >> (bytes * 8 - k) # trim excess bits - def _stub(self, *args, **kwds): + def seed(self, *args, **kwds): "Stub method. Not used for a system random number generator." return None - seed = jumpahead = _stub def _notimplemented(self, *args, **kwds): "Method should not be called for a system random number generator." @@ -866,7 +709,6 @@ paretovariate = _inst.paretovariate weibullvariate = _inst.weibullvariate getstate = _inst.getstate setstate = _inst.setstate -jumpahead = _inst.jumpahead getrandbits = _inst.getrandbits if __name__ == '__main__': diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index b6d2ec0..892535d 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -444,7 +444,7 @@ Subject: Re: PEP 255: Simple Generators >>> roots = sets[:] >>> import random ->>> gen = random.WichmannHill(42) +>>> gen = random.Random(42) >>> while 1: ... for s in sets: ... print(" %s->%s" % (s, s.find()), end='') @@ -458,29 +458,29 @@ Subject: Re: PEP 255: Simple Generators ... else: ... break A->A B->B C->C D->D E->E F->F G->G H->H I->I J->J K->K L->L M->M -merged D into G - A->A B->B C->C D->G E->E F->F G->G H->H I->I J->J K->K L->L M->M -merged C into F - A->A B->B C->F D->G E->E F->F G->G H->H I->I J->J K->K L->L M->M +merged I into A + A->A B->B C->C D->D E->E F->F G->G H->H I->A J->J K->K L->L M->M +merged D into C + A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->K L->L M->M +merged K into H + A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->H L->L M->M merged L into A - A->A B->B C->F D->G E->E F->F G->G H->H I->I J->J K->K L->A M->M -merged H into E - A->A B->B C->F D->G E->E F->F G->G H->E I->I J->J K->K L->A M->M -merged B into E - A->A B->E C->F D->G E->E F->F G->G H->E I->I J->J K->K L->A M->M + A->A B->B C->C D->C E->E F->F G->G H->H I->A J->J K->H L->A M->M +merged E into A + A->A B->B C->C D->C E->A F->F G->G H->H I->A J->J K->H L->A M->M +merged B into G + A->A B->G C->C D->C E->A F->F G->G H->H I->A J->J K->H L->A M->M +merged A into F + A->F B->G C->C D->C E->F F->F G->G H->H I->F J->J K->H L->F M->M +merged H into G + A->F B->G C->C D->C E->F F->F G->G H->G I->F J->J K->G L->F M->M +merged F into J + A->J B->G C->C D->C E->J F->J G->G H->G I->J J->J K->G L->J M->M +merged M into C + A->J B->G C->C D->C E->J F->J G->G H->G I->J J->J K->G L->J M->C merged J into G - A->A B->E C->F D->G E->E F->F G->G H->E I->I J->G K->K L->A M->M -merged E into G - A->A B->G C->F D->G E->G F->F G->G H->G I->I J->G K->K L->A M->M -merged M into G - A->A B->G C->F D->G E->G F->F G->G H->G I->I J->G K->K L->A M->G -merged I into K - A->A B->G C->F D->G E->G F->F G->G H->G I->K J->G K->K L->A M->G -merged K into A - A->A B->G C->F D->G E->G F->F G->G H->G I->A J->G K->A L->A M->G -merged F into A - A->A B->G C->A D->G E->G F->A G->G H->G I->A J->G K->A L->A M->G -merged A into G + A->G B->G C->C D->C E->G F->G G->G H->G I->G J->G K->G L->G M->C +merged C into G A->G B->G C->G D->G E->G F->G G->G H->G I->G J->G K->G L->G M->G """ diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 6adcd06..a7fe605 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -42,21 +42,6 @@ class TestBasicOps(unittest.TestCase): self.assertRaises(TypeError, self.gen.seed, 1, 2) self.assertRaises(TypeError, type(self.gen), []) - def test_jumpahead(self): - self.gen.seed() - state1 = self.gen.getstate() - self.gen.jumpahead(100) - state2 = self.gen.getstate() # s/b distinct from state1 - self.assertNotEqual(state1, state2) - self.gen.jumpahead(100) - state3 = self.gen.getstate() # s/b distinct from state2 - self.assertNotEqual(state2, state3) - - self.assertRaises(TypeError, self.gen.jumpahead) # needs an arg - self.assertRaises(TypeError, self.gen.jumpahead, "ick") # wrong type - self.assertRaises(TypeError, self.gen.jumpahead, 2.3) # wrong type - self.assertRaises(TypeError, self.gen.jumpahead, 2, 3) # too many - def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that # the sample is of the correct length and contains only unique items @@ -157,48 +142,6 @@ class TestBasicOps(unittest.TestCase): f.close() self.assertEqual(r.randrange(1000), value) -class WichmannHill_TestBasicOps(TestBasicOps): - gen = random.WichmannHill() - - def test_setstate_first_arg(self): - self.assertRaises(ValueError, self.gen.setstate, (2, None, None)) - - def test_strong_jumpahead(self): - # tests that jumpahead(n) semantics correspond to n calls to random() - N = 1000 - s = self.gen.getstate() - self.gen.jumpahead(N) - r1 = self.gen.random() - # now do it the slow way - self.gen.setstate(s) - for i in range(N): - self.gen.random() - r2 = self.gen.random() - self.assertEqual(r1, r2) - - def test_gauss_with_whseed(self): - # Ensure that the seed() method initializes all the hidden state. In - # particular, through 2.2.1 it failed to reset a piece of state used - # by (and only by) the .gauss() method. - - for seed in 1, 12, 123, 1234, 12345, 123456, 654321: - self.gen.whseed(seed) - x1 = self.gen.random() - y1 = self.gen.gauss(0, 1) - - self.gen.whseed(seed) - x2 = self.gen.random() - y2 = self.gen.gauss(0, 1) - - self.assertEqual(x1, x2) - self.assertEqual(y1, y2) - - def test_bigrand(self): - # Verify warnings are raised when randrange is too large for random() - with test_support.catch_warning(): - warnings.filterwarnings("error", "Underlying random") - self.assertRaises(UserWarning, self.gen.randrange, 2**60) - class SystemRandom_TestBasicOps(TestBasicOps): gen = random.SystemRandom() @@ -214,10 +157,6 @@ class SystemRandom_TestBasicOps(TestBasicOps): # Doesn't need to do anything except not fail self.gen.seed(100) - def test_jumpahead(self): - # Doesn't need to do anything except not fail - self.gen.jumpahead(100) - def test_gauss(self): self.gen.gauss_next = None self.gen.seed(100) @@ -541,8 +480,7 @@ class TestModule(unittest.TestCase): def test_main(verbose=None): - testclasses = [WichmannHill_TestBasicOps, - MersenneTwister_TestBasicOps, + testclasses = [MersenneTwister_TestBasicOps, TestDistributions, TestModule] @@ -352,6 +352,9 @@ Core and Builtins Library ------- +- Removed defunct parts of the random module (the Wichmann-Hill generator + and the jumpahead() method). + - Patch #467924: add ZipFile.extract() and ZipFile.extractall() in the zipfile module. diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 957422c..77b238d 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -369,72 +369,6 @@ random_setstate(RandomObject *self, PyObject *state) return Py_None; } -/* -Jumpahead should be a fast way advance the generator n-steps ahead, but -lacking a formula for that, the next best is to use n and the existing -state to create a new state far away from the original. - -The generator uses constant spaced additive feedback, so shuffling the -state elements ought to produce a state which would not be encountered -(in the near term) by calls to random(). Shuffling is normally -implemented by swapping the ith element with another element ranging -from 0 to i inclusive. That allows the element to have the possibility -of not being moved. Since the goal is to produce a new, different -state, the swap element is ranged from 0 to i-1 inclusive. This assures -that each element gets moved at least once. - -To make sure that consecutive calls to jumpahead(n) produce different -states (even in the rare case of involutory shuffles), i+1 is added to -each element at position i. Successive calls are then guaranteed to -have changing (growing) values as well as shuffled positions. - -Finally, the self->index value is set to N so that the generator itself -kicks in on the next call to random(). This assures that all results -have been through the generator and do not just reflect alterations to -the underlying state. -*/ - -static PyObject * -random_jumpahead(RandomObject *self, PyObject *n) -{ - long i, j; - PyObject *iobj; - PyObject *remobj; - unsigned long *mt, tmp; - - if (!PyLong_Check(n)) { - PyErr_Format(PyExc_TypeError, "jumpahead requires an " - "integer, not '%s'", - Py_TYPE(n)->tp_name); - return NULL; - } - - mt = self->state; - for (i = N-1; i > 1; i--) { - iobj = PyLong_FromLong(i); - if (iobj == NULL) - return NULL; - remobj = PyNumber_Remainder(n, iobj); - Py_DECREF(iobj); - if (remobj == NULL) - return NULL; - j = PyLong_AsLong(remobj); - Py_DECREF(remobj); - if (j == -1L && PyErr_Occurred()) - return NULL; - tmp = mt[i]; - mt[i] = mt[j]; - mt[j] = tmp; - } - - for (i = 0; i < N; i++) - mt[i] += i+1; - - self->index = N; - Py_INCREF(Py_None); - return Py_None; -} - static PyObject * random_getrandbits(RandomObject *self, PyObject *args) { @@ -506,9 +440,6 @@ static PyMethodDef random_methods[] = { PyDoc_STR("getstate() -> tuple containing the current state.")}, {"setstate", (PyCFunction)random_setstate, METH_O, PyDoc_STR("setstate(state) -> None. Restores generator state.")}, - {"jumpahead", (PyCFunction)random_jumpahead, METH_O, - PyDoc_STR("jumpahead(int) -> None. Create new state from " - "existing state and integer.")}, {"getrandbits", (PyCFunction)random_getrandbits, METH_VARARGS, PyDoc_STR("getrandbits(k) -> x. Generates a long int with " "k random bits.")}, |