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