home *** CD-ROM | disk | FTP | other *** search
- """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
-
-
- USE:
- whrandom.random() yields double precision random numbers
- uniformly distributed between 0 and 1.
-
- whrandom.seed(x, y, z) must be called before whrandom.random()
- to seed the generator
-
- There is also an interface to create multiple independent
- random generators, and to choose from other ranges.
-
-
-
- Multi-threading note: the random number generator used here is not
- thread-safe; it is possible that nearly simultaneous calls in
- different theads return the same random value. To avoid this, you
- have to use a lock around all calls. (I didn't want to slow this
- down in the serial case by using a lock here.)
- """
-
- # Translated by Guido van Rossum from C source provided by
- # Adrian Baddeley.
-
-
- class whrandom:
- def __init__(self, x = 0, y = 0, z = 0):
- """Initialize an instance.
- Without arguments, initialize from current time.
- With arguments (x, y, z), initialize from them."""
- self.seed(x, y, z)
-
- def seed(self, x = 0, y = 0, z = 0):
- """Set the 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 random(self):
- """Get the next random number in the range [0.0, 1.0)."""
- # 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
- #
- return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
-
- def uniform(self, a, b):
- """Get a random number in the range [a, b)."""
- return a + (b-a) * self.random()
-
- def randint(self, a, b):
- """Get a random integer in the range [a, b] including
- both end points.
-
- (Deprecated; use randrange below.)"""
- return self.randrange(a, b+1)
-
- def choice(self, seq):
- """Choose a random element from a non-empty sequence."""
- return seq[int(self.random() * len(seq))]
-
- def randrange(self, start, stop=None, step=1, int=int, default=None):
- """Choose a random item from range(start, stop[, step]).
-
- This fixes the problem with randint() which includes the
- endpoint; in Python this is usually not what you want.
- Do not supply the 'int' and 'default' arguments."""
- # This code is a bit messy to make it fast for the
- # common case while still doing adequate error checking
- istart = int(start)
- if istart != start:
- raise ValueError, "non-integer arg 1 for randrange()"
- if stop is default:
- if istart > 0:
- return int(self.random() * istart)
- raise ValueError, "empty range for randrange()"
- istop = int(stop)
- if istop != stop:
- raise ValueError, "non-integer stop for randrange()"
- if step == 1:
- if istart < istop:
- return istart + int(self.random() *
- (istop - istart))
- raise ValueError, "empty range for randrange()"
- istep = int(step)
- if istep != step:
- raise ValueError, "non-integer step for randrange()"
- if istep > 0:
- n = (istop - istart + istep - 1) / istep
- elif istep < 0:
- n = (istop - istart + istep + 1) / istep
- else:
- raise ValueError, "zero step for randrange()"
-
- if n <= 0:
- raise ValueError, "empty range for randrange()"
- return istart + istep*int(self.random() * n)
-
-
- # Initialize from the current time
- _inst = whrandom()
- seed = _inst.seed
- random = _inst.random
- uniform = _inst.uniform
- randint = _inst.randint
- choice = _inst.choice
- randrange = _inst.randrange
-