diff options
| author | Tim Peters <tim.peters@gmail.com> | 2001-01-25 06:23:18 (GMT) | 
|---|---|---|
| committer | Tim Peters <tim.peters@gmail.com> | 2001-01-25 06:23:18 (GMT) | 
| commit | d52269bfd029c4a517ea74c17edd5c3a250c366c (patch) | |
| tree | f7a508e0b89861d7f3adaaad2f37aa255ba3c173 /Lib/random.py | |
| parent | d7b5e88e8e40b77813ceb25dc28b87d672538403 (diff) | |
| download | cpython-d52269bfd029c4a517ea74c17edd5c3a250c366c.zip cpython-d52269bfd029c4a517ea74c17edd5c3a250c366c.tar.gz cpython-d52269bfd029c4a517ea74c17edd5c3a250c366c.tar.bz2  | |
Fix bugs introduced by rewrite (in particular, time-based initialization
got broken).  Also added new method .jumpahead(N).  This finally gives us
a semi-decent answer to how Python's RNGs can be used safely and efficiently
in multithreaded programs (although it requires the user to use the new
machinery!).
Diffstat (limited to 'Lib/random.py')
| -rw-r--r-- | Lib/random.py | 42 | 
1 files changed, 38 insertions, 4 deletions
diff --git a/Lib/random.py b/Lib/random.py index a818f73..1b91886 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -72,8 +72,8 @@ class Random:          self.gauss_next = None      # Specific to Wichmann-Hill generator.  Subclasses wishing to use a -    # different core generator should override seed(), random(),  getstate() -    # and setstate(). +    # different core generator should override the seed(), random(), +    # getstate(), setstate(), and jumpahead() methods.      def __whseed(self, x=0, y=0, z=0):          """Set the Wichmann-Hill seed from (x, y, z). @@ -104,6 +104,7 @@ class Random:          if a is None:              self.__whseed() +            return          a = hash(a)          a, x = divmod(a, 256)          a, y = divmod(a, 256) @@ -115,11 +116,10 @@ class Random:      def getstate(self):          """Return internal state; can be passed to setstate() later.""" -          return self.VERSION, self._seed, self.gauss_next      def __getstate__(self): # for pickle -        self.getstate() +        return self.getstate()      def setstate(self, state):          """Restore internal state from object returned by getstate().""" @@ -134,6 +134,28 @@ class Random:      def __setstate__(self, state):  # for pickle          self.setstate(state) +    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 random(self):          """Get the next random number in the range [0.0, 1.0).""" @@ -471,6 +493,17 @@ def _test_generator(n, funccall):      print 'avg %g, stddev %g, min %g, max %g' % \                (avg, stddev, smallest, largest) +    s = getstate() +    N = 1019 +    jumpahead(N) +    r1 = random() +    setstate(s) +    for i in range(N):  # now do it the slow way +        random() +    r2 = random() +    if r1 != r2: +        raise ValueError("jumpahead test failed " + `(N, r1, r2)`) +  def _test(N=200):      print 'TWOPI         =', TWOPI      print 'LOG4          =', LOG4 @@ -515,6 +548,7 @@ paretovariate = _inst.paretovariate  weibullvariate = _inst.weibullvariate  getstate = _inst.getstate  setstate = _inst.setstate +jumpahead = _inst.jumpahead  if __name__ == '__main__':      _test()  | 
