Change random.seed() so that it can get at the full range of possible
internal states. Put the old .seed() (which could only get at about the square root of the # of possibilities) under the new name .whseed(), for bit-level compatibility with older versions. This occurred to me while reviewing effbot's book (he found himself stumbling over .seed() more than once there ...).
This commit is contained in:
parent
0eb107068a
commit
0de88fc4b1
|
@ -25,7 +25,8 @@ numbers it generates before repeating the sequence exactly) is
|
||||||
6,953,607,871,644. While of much higher quality than the \function{rand()}
|
6,953,607,871,644. While of much higher quality than the \function{rand()}
|
||||||
function supplied by most C libraries, the theoretical properties
|
function supplied by most C libraries, the theoretical properties
|
||||||
are much the same as for a single linear congruential generator of
|
are much the same as for a single linear congruential generator of
|
||||||
large modulus.
|
large modulus. It is not suitable for all purposes, and is completely
|
||||||
|
unsuitable for cryptographic purposes.
|
||||||
|
|
||||||
The functions in this module are not threadsafe: if you want to call these
|
The functions in this module are not threadsafe: if you want to call these
|
||||||
functions from multiple threads, you should explicitly serialize the calls.
|
functions from multiple threads, you should explicitly serialize the calls.
|
||||||
|
@ -72,7 +73,7 @@ gens = create_generators(10, 1000000)
|
||||||
That creates 10 distinct generators, which can be passed out to 10 distinct
|
That creates 10 distinct generators, which can be passed out to 10 distinct
|
||||||
threads. The generators don't share state so can be called safely in
|
threads. The generators don't share state so can be called safely in
|
||||||
parallel. So long as no thread calls its \code{g.random()} more than a
|
parallel. So long as no thread calls its \code{g.random()} more than a
|
||||||
million times (the second argument to \function{create_generators}), the
|
million times (the second argument to \function{create_generators), the
|
||||||
sequences seen by each thread will not overlap. The period of the
|
sequences seen by each thread will not overlap. The period of the
|
||||||
underlying Wichmann-Hill generator limits how far this technique can be
|
underlying Wichmann-Hill generator limits how far this technique can be
|
||||||
pushed.
|
pushed.
|
||||||
|
@ -83,10 +84,10 @@ also be used to "move backward in time":
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
>>> g = Random(42) # arbitrary
|
>>> g = Random(42) # arbitrary
|
||||||
>>> g.random()
|
>>> g.random()
|
||||||
0.24855401895528142
|
0.25420336316883324
|
||||||
>>> g.jumpahead(6953607871644L - 1) # move *back* one
|
>>> g.jumpahead(6953607871644L - 1) # move *back* one
|
||||||
>>> g.random()
|
>>> g.random()
|
||||||
0.24855401895528142
|
0.25420336316883324
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,25 +95,38 @@ Bookkeeping functions:
|
||||||
|
|
||||||
\begin{funcdesc}{seed}{\optional{x}}
|
\begin{funcdesc}{seed}{\optional{x}}
|
||||||
Initialize the basic random number generator.
|
Initialize the basic random number generator.
|
||||||
Optional argument \var{x} can be any hashable object,
|
Optional argument \var{x} can be any hashable object.
|
||||||
and the generator is seeded from its hash code.
|
If \var(x) is omitted or \code{None}, current system time is used;
|
||||||
It is not guaranteed that distinct hash codes will produce distinct
|
current system time is also used to initialize the generator when the
|
||||||
seeds.
|
module is first imported.
|
||||||
If \var{x} is omitted or \code{None},
|
If \var(x) is not \code{None} or an int or long,
|
||||||
the seed is derived from the current system time.
|
\code{hash(\var{x})) is used instead.
|
||||||
The seed is also set from the current system time when
|
If \var{x} is an int or long, \var{x} is used directly.
|
||||||
the module is first imported.
|
Distinct values between 0 and 27814431486575L inclusive are guaranteed
|
||||||
|
to yield distinct internal states (this guarantee is specific to the
|
||||||
|
default Wichmann-Hill generator, and may not apply to subclasses
|
||||||
|
supplying their own basic generator).
|
||||||
|
\end{funcdesc}
|
||||||
|
|
||||||
|
\begin{funcdesc}{whseed}{\optional{x}}
|
||||||
|
This is obsolete, supplied for bit-level compatibility with versions
|
||||||
|
of Python prior to 2.1.
|
||||||
|
See \function{seed} for details. \function{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.
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{getstate}{}
|
\begin{funcdesc}{getstate}{}
|
||||||
Return an object capturing the current internal state of the generator.
|
Return an object capturing the current internal state of the generator.
|
||||||
This object can be passed to \code{setstate()} to restore the state.
|
This object can be passed to \code{setstate()} to restore the state.
|
||||||
\end{funcdesc}
|
\versionadded{2.1}
|
||||||
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{setstate}{state}
|
\begin{funcdesc}{setstate}{state}
|
||||||
\var{state} should have been obtained from a previous call to
|
\var{state} should have been obtained from a previous call to
|
||||||
\code{getstate()}, and \code{setstate()} restores the internal state
|
\code{getstate()}, and \code{setstate()} restores the internal state
|
||||||
of the generate to what it was at the time \code{setstate()} was called.
|
of the generator to what it was at the time \code{setstate()} was called.
|
||||||
|
\versionadded{2.1}
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{jumpahead}{n}
|
\begin{funcdesc}{jumpahead}{n}
|
||||||
|
@ -124,6 +138,7 @@ Bookkeeping functions:
|
||||||
internal state, and then \method{jumpahead()} can be used to force the
|
internal state, and then \method{jumpahead()} can be used to force the
|
||||||
instances' states as far apart as you like (up to the period of the
|
instances' states as far apart as you like (up to the period of the
|
||||||
generator).
|
generator).
|
||||||
|
\versionadded{2.1}
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
Functions for integers:
|
Functions for integers:
|
||||||
|
|
104
Lib/random.py
104
Lib/random.py
|
@ -66,10 +66,10 @@ used to "move backward in time":
|
||||||
|
|
||||||
>>> g = Random(42) # arbitrary
|
>>> g = Random(42) # arbitrary
|
||||||
>>> g.random()
|
>>> g.random()
|
||||||
0.24855401895528142
|
0.25420336316883324
|
||||||
>>> g.jumpahead(6953607871644L - 1) # move *back* one
|
>>> g.jumpahead(6953607871644L - 1) # move *back* one
|
||||||
>>> g.random()
|
>>> g.random()
|
||||||
0.24855401895528142
|
0.25420336316883324
|
||||||
"""
|
"""
|
||||||
# XXX The docstring sucks.
|
# XXX The docstring sucks.
|
||||||
|
|
||||||
|
@ -119,26 +119,31 @@ class Random:
|
||||||
# different core generator should override the seed(), random(),
|
# different core generator should override the seed(), random(),
|
||||||
# getstate(), setstate() and jumpahead() methods.
|
# getstate(), setstate() and jumpahead() methods.
|
||||||
|
|
||||||
def __whseed(self, x=0, y=0, z=0):
|
def seed(self, a=None):
|
||||||
"""Set the Wichmann-Hill seed from (x, y, z).
|
"""Initialize internal state from hashable object.
|
||||||
|
|
||||||
These must be integers in the range [0, 256).
|
None or no argument seeds from current time.
|
||||||
|
|
||||||
|
If a is not None or an int or long, hash(a) is 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 not type(x) == type(y) == type(z) == type(0):
|
if a is None:
|
||||||
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
|
# Initialize from current time
|
||||||
import time
|
import time
|
||||||
t = long(time.time()) * 256
|
a = long(time.time() * 256)
|
||||||
t = int((t&0xffffff) ^ (t>>24))
|
|
||||||
t, x = divmod(t, 256)
|
if type(a) not in (type(3), type(3L)):
|
||||||
t, y = divmod(t, 256)
|
a = hash(a)
|
||||||
t, z = divmod(t, 256)
|
|
||||||
# Zero is a poor seed, so substitute 1
|
a, x = divmod(a, 30268)
|
||||||
self._seed = (x or 1, y or 1, z or 1)
|
a, y = divmod(a, 30306)
|
||||||
|
a, z = divmod(a, 30322)
|
||||||
|
self._seed = int(x)+1, int(y)+1, int(z)+1
|
||||||
|
|
||||||
def random(self):
|
def random(self):
|
||||||
"""Get the next random number in the range [0.0, 1.0)."""
|
"""Get the next random number in the range [0.0, 1.0)."""
|
||||||
|
@ -171,26 +176,6 @@ class Random:
|
||||||
# never return 0.0 (asserted by Tim; proof too long for a comment).
|
# 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
|
return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
|
||||||
|
|
||||||
def seed(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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def getstate(self):
|
def getstate(self):
|
||||||
"""Return internal state; can be passed to setstate() later."""
|
"""Return internal state; can be passed to setstate() later."""
|
||||||
return self.VERSION, self._seed, self.gauss_next
|
return self.VERSION, self._seed, self.gauss_next
|
||||||
|
@ -227,6 +212,50 @@ class Random:
|
||||||
z = int(z * pow(170, n, 30323)) % 30323
|
z = int(z * pow(170, n, 30323)) % 30323
|
||||||
self._seed = x, y, z
|
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) == type(0):
|
||||||
|
raise TypeError('seeds must be integers')
|
||||||
|
if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256):
|
||||||
|
raise ValueError('seeds must be in range(0, 256)')
|
||||||
|
if 0 == x == y == z:
|
||||||
|
# Initialize from current time
|
||||||
|
import time
|
||||||
|
t = long(time.time() * 256)
|
||||||
|
t = int((t&0xffffff) ^ (t>>24))
|
||||||
|
t, x = divmod(t, 256)
|
||||||
|
t, y = divmod(t, 256)
|
||||||
|
t, z = divmod(t, 256)
|
||||||
|
# Zero is a poor seed, so substitute 1
|
||||||
|
self._seed = (x or 1, y or 1, z or 1)
|
||||||
|
|
||||||
|
def 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)
|
||||||
|
|
||||||
## ---- Methods below this point do not need to be overridden when
|
## ---- Methods below this point do not need to be overridden when
|
||||||
## ---- subclassing for the purpose of using a different core generator.
|
## ---- subclassing for the purpose of using a different core generator.
|
||||||
|
|
||||||
|
@ -623,6 +652,7 @@ weibullvariate = _inst.weibullvariate
|
||||||
getstate = _inst.getstate
|
getstate = _inst.getstate
|
||||||
setstate = _inst.setstate
|
setstate = _inst.setstate
|
||||||
jumpahead = _inst.jumpahead
|
jumpahead = _inst.jumpahead
|
||||||
|
whseed = _inst.whseed
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
_test()
|
_test()
|
||||||
|
|
10
Misc/NEWS
10
Misc/NEWS
|
@ -32,6 +32,16 @@ Standard library
|
||||||
each thread, then using .jumpahead() to force each instance to use a
|
each thread, then using .jumpahead() to force each instance to use a
|
||||||
non-overlapping segment of the full period.
|
non-overlapping segment of the full period.
|
||||||
|
|
||||||
|
- random.py's seed() function is new. For bit-for-bit compatibility with
|
||||||
|
prior releases, use the whseed function instead. The new seed function
|
||||||
|
addresses two problems: (1) The old function couldn't produce more than
|
||||||
|
about 2**24 distinct internal states; the new one about 2**45 (the best
|
||||||
|
that can be done in the Wichmann-Hill generator). (2) The old function
|
||||||
|
sometimes produced identical internal states when passed distinct
|
||||||
|
integers, and there was no simple way to predict when that would happen;
|
||||||
|
the new one guarantees to produce distinct internal states for all
|
||||||
|
arguments in [0, 27814431486576L).
|
||||||
|
|
||||||
Windows changes
|
Windows changes
|
||||||
|
|
||||||
- Build procedure: the zlib project is built in a different way that
|
- Build procedure: the zlib project is built in a different way that
|
||||||
|
|
Loading…
Reference in New Issue