home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pytho152.zip / emx / lib / python1.5 / whrandom.py < prev    next >
Text File  |  2000-08-10  |  4KB  |  147 lines

  1. #    WICHMANN-HILL RANDOM NUMBER GENERATOR
  2. #
  3. #    Wichmann, B. A. & Hill, I. D. (1982)
  4. #    Algorithm AS 183: 
  5. #    An efficient and portable pseudo-random number generator
  6. #    Applied Statistics 31 (1982) 188-190
  7. #
  8. #    see also: 
  9. #        Correction to Algorithm AS 183
  10. #        Applied Statistics 33 (1984) 123  
  11. #
  12. #        McLeod, A. I. (1985)
  13. #        A remark on Algorithm AS 183 
  14. #        Applied Statistics 34 (1985),198-200
  15. #
  16. #
  17. #    USE:
  18. #    whrandom.random()    yields double precision random numbers 
  19. #                uniformly distributed between 0 and 1.
  20. #
  21. #    whrandom.seed(x, y, z)    must be called before whrandom.random()
  22. #                to seed the generator
  23. #
  24. #    There is also an interface to create multiple independent
  25. #    random generators, and to choose from other ranges.
  26.  
  27.  
  28. #    Translated by Guido van Rossum from C source provided by
  29. #    Adrian Baddeley.
  30.  
  31.  
  32. # Multi-threading note: the random number generator used here is not
  33. # thread-safe; it is possible that nearly simultaneous calls in
  34. # different theads return the same random value.  To avoid this, you
  35. # have to use a lock around all calls.  (I didn't want to slow this
  36. # down in the serial case by using a lock here.)
  37.  
  38.  
  39. class whrandom:
  40.     #
  41.     # Initialize an instance.
  42.     # Without arguments, initialize from current time.
  43.     # With arguments (x, y, z), initialize from them.
  44.     #
  45.     def __init__(self, x = 0, y = 0, z = 0):
  46.         self.seed(x, y, z)
  47.     #
  48.     # Set the seed from (x, y, z).
  49.     # These must be integers in the range [0, 256).
  50.     #
  51.     def seed(self, x = 0, y = 0, z = 0):
  52.         if not type(x) == type(y) == type(z) == type(0):
  53.             raise TypeError, 'seeds must be integers'
  54.         if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256):
  55.             raise ValueError, 'seeds must be in range(0, 256)'
  56.         if 0 == x == y == z:
  57.             # Initialize from current time
  58.             import time
  59.             t = long(time.time() * 256)
  60.             t = int((t&0xffffff) ^ (t>>24))
  61.             t, x = divmod(t, 256)
  62.             t, y = divmod(t, 256)
  63.             t, z = divmod(t, 256)
  64.         # Zero is a poor seed, so substitute 1
  65.         self._seed = (x or 1, y or 1, z or 1)
  66.     #
  67.     # Get the next random number in the range [0.0, 1.0).
  68.     #
  69.     def random(self):
  70.         # This part is thread-unsafe:
  71.         # BEGIN CRITICAL SECTION
  72.         x, y, z = self._seed
  73.         #
  74.         x = (171 * x) % 30269
  75.         y = (172 * y) % 30307
  76.         z = (170 * z) % 30323
  77.         #
  78.         self._seed = x, y, z
  79.         # END CRITICAL SECTION
  80.         #
  81.         return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
  82.     #
  83.     # Get a random number in the range [a, b).
  84.     #
  85.     def uniform(self, a, b):
  86.         return a + (b-a) * self.random()
  87.     #
  88.     # Get a random integer in the range [a, b] including both end points.
  89.     # (Deprecated; use randrange below.)
  90.     #
  91.     def randint(self, a, b):
  92.         return self.randrange(a, b+1)
  93.     #
  94.     # Choose a random element from a non-empty sequence.
  95.     #
  96.     def choice(self, seq):
  97.         return seq[int(self.random() * len(seq))]
  98.     #
  99.     # Choose a random item from range([start,] step[, stop]).
  100.     # This fixes the problem with randint() which includes the
  101.     # endpoint; in Python this is usually not what you want.
  102.     #
  103.     def randrange(self, start, stop=None, step=1,
  104.               # Do not supply the following arguments
  105.               int=int, default=None):
  106.         # This code is a bit messy to make it fast for the
  107.         # common case while still doing adequate error checking
  108.         istart = int(start)
  109.         if istart != start:
  110.             raise ValueError, "non-integer arg 1 for randrange()"
  111.         if stop is default:
  112.             if istart > 0:
  113.                 return int(self.random() * istart)
  114.             raise ValueError, "empty range for randrange()"
  115.         istop = int(stop)
  116.         if istop != stop:
  117.             raise ValueError, "non-integer stop for randrange()"
  118.         if step == 1:
  119.             if istart < istop:
  120.                 return istart + int(self.random() *
  121.                            (istop - istart))
  122.             raise ValueError, "empty range for randrange()"
  123.         istep = int(step)
  124.         if istep != step:
  125.             raise ValueError, "non-integer step for randrange()"
  126.         if istep > 0:
  127.             n = (istop - istart + istep - 1) / istep
  128.         elif istep < 0:
  129.             n = (istop - istart + istep + 1) / istep
  130.         else:
  131.             raise ValueError, "zero step for randrange()"
  132.  
  133.         if n <= 0:
  134.             raise ValueError, "empty range for randrange()"
  135.         return istart + istep*int(self.random() * n)
  136.  
  137.  
  138. # Initialize from the current time
  139. #
  140. _inst = whrandom()
  141. seed = _inst.seed
  142. random = _inst.random
  143. uniform = _inst.uniform
  144. randint = _inst.randint
  145. choice = _inst.choice
  146. randrange = _inst.randrange
  147.