[Python-checkins] CVS: python/dist/src/Lib random.py,1.16,1.17

Tim Peters tim_one@users.sourceforge.net
Wed, 24 Jan 2001 22:23:20 -0800


Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv11428/python/dist/src/Lib

Modified Files:
	random.py 
Log Message:
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!).


Index: random.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/random.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -r1.16 -r1.17
*** random.py	2001/01/25 03:36:25	1.16
--- random.py	2001/01/25 06:23:18	1.17
***************
*** 73,78 ****
  
      # Specific to Wichmann-Hill generator.  Subclasses wishing to use a
!     # different core generator should override seed(), random(),  getstate()
!     # and setstate().
  
      def __whseed(self, x=0, y=0, z=0):
--- 73,78 ----
  
      # Specific to Wichmann-Hill generator.  Subclasses wishing to use a
!     # different core generator should override the seed(), random(),
!     # getstate(), setstate(), and jumpahead() methods.
  
      def __whseed(self, x=0, y=0, z=0):
***************
*** 105,108 ****
--- 105,109 ----
          if a is None:
              self.__whseed()
+             return
          a = hash(a)
          a, x = divmod(a, 256)
***************
*** 116,124 ****
      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()
  
      def setstate(self, state):
--- 117,124 ----
      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
!         return self.getstate()
  
      def setstate(self, state):
***************
*** 135,138 ****
--- 135,160 ----
          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)."""
***************
*** 472,475 ****
--- 494,508 ----
                (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
***************
*** 516,519 ****
--- 549,553 ----
  getstate = _inst.getstate
  setstate = _inst.setstate
+ jumpahead = _inst.jumpahead
  
  if __name__ == '__main__':