blob: fd9b1a9390f2f041278890007880b71f27eca5d6 [file] [log] [blame]
Guido van Rossume7b146f2000-02-04 15:28:42 +00001"""Wichman-Hill random number generator.
2
3Wichmann, B. A. & Hill, I. D. (1982)
4Algorithm AS 183:
5An efficient and portable pseudo-random number generator
6Applied Statistics 31 (1982) 188-190
7
8see 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
Guido van Rossumc6360141990-10-13 19:23:40 +000015
16
Guido van Rossume7b146f2000-02-04 15:28:42 +000017USE:
18whrandom.random() yields double precision random numbers
19 uniformly distributed between 0 and 1.
20
21whrandom.seed(x, y, z) must be called before whrandom.random()
22 to seed the generator
23
24There is also an interface to create multiple independent
25random generators, and to choose from other ranges.
Guido van Rossumc6360141990-10-13 19:23:40 +000026
27
Guido van Rossume7b146f2000-02-04 15:28:42 +000028Translated by Guido van Rossum from C source provided by
29Adrian Baddeley.
Guido van Rossumcc20b761998-05-29 17:51:59 +000030
31
Guido van Rossume7b146f2000-02-04 15:28:42 +000032
33Multi-threading note: the random number generator used here is not
34thread-safe; it is possible that nearly simultaneous calls in
35different theads return the same random value. To avoid this, you
36have to use a lock around all calls. (I didn't want to slow this
37down in the serial case by using a lock here.)
38"""
39
Guido van Rossum2db91351992-10-18 17:09:59 +000040class whrandom:
Guido van Rossum14a6e3d1994-09-14 13:33:57 +000041 def __init__(self, x = 0, y = 0, z = 0):
Guido van Rossume7b146f2000-02-04 15:28:42 +000042 """Initialize an instance.
43 Without arguments, initialize from current time.
44 With arguments (x, y, z), initialize from them."""
Guido van Rossum14a6e3d1994-09-14 13:33:57 +000045 self.seed(x, y, z)
Guido van Rossume7b146f2000-02-04 15:28:42 +000046
Guido van Rossum14a6e3d1994-09-14 13:33:57 +000047 def seed(self, x = 0, y = 0, z = 0):
Guido van Rossume7b146f2000-02-04 15:28:42 +000048 """Set the seed from (x, y, z).
49 These must be integers in the range [0, 256)."""
Guido van Rossum14a6e3d1994-09-14 13:33:57 +000050 if not type(x) == type(y) == type(z) == type(0):
51 raise TypeError, 'seeds must be integers'
Guido van Rossum67577481997-01-02 18:13:35 +000052 if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256):
Guido van Rossum14a6e3d1994-09-14 13:33:57 +000053 raise ValueError, 'seeds must be in range(0, 256)'
54 if 0 == x == y == z:
Guido van Rossum2db91351992-10-18 17:09:59 +000055 # Initialize from current time
56 import time
Guido van Rossum9cb018e1996-10-21 23:20:03 +000057 t = long(time.time() * 256)
Guido van Rossum358473c1998-02-16 14:52:42 +000058 t = int((t&0xffffff) ^ (t>>24))
Guido van Rossum2db91351992-10-18 17:09:59 +000059 t, x = divmod(t, 256)
60 t, y = divmod(t, 256)
61 t, z = divmod(t, 256)
Guido van Rossumd2478121997-07-10 15:14:50 +000062 # Zero is a poor seed, so substitute 1
63 self._seed = (x or 1, y or 1, z or 1)
Guido van Rossume7b146f2000-02-04 15:28:42 +000064
Guido van Rossum2db91351992-10-18 17:09:59 +000065 def random(self):
Guido van Rossume7b146f2000-02-04 15:28:42 +000066 """Get the next random number in the range [0.0, 1.0)."""
Guido van Rossumcc20b761998-05-29 17:51:59 +000067 # This part is thread-unsafe:
68 # BEGIN CRITICAL SECTION
Guido van Rossum2db91351992-10-18 17:09:59 +000069 x, y, z = self._seed
70 #
Guido van Rossumd2478121997-07-10 15:14:50 +000071 x = (171 * x) % 30269
72 y = (172 * y) % 30307
73 z = (170 * z) % 30323
Guido van Rossum2db91351992-10-18 17:09:59 +000074 #
75 self._seed = x, y, z
Guido van Rossumcc20b761998-05-29 17:51:59 +000076 # END CRITICAL SECTION
Guido van Rossum2db91351992-10-18 17:09:59 +000077 #
78 return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
Guido van Rossume7b146f2000-02-04 15:28:42 +000079
Guido van Rossum2db91351992-10-18 17:09:59 +000080 def uniform(self, a, b):
Guido van Rossume7b146f2000-02-04 15:28:42 +000081 """Get a random number in the range [a, b)."""
Guido van Rossum2db91351992-10-18 17:09:59 +000082 return a + (b-a) * self.random()
Guido van Rossume7b146f2000-02-04 15:28:42 +000083
Guido van Rossum2db91351992-10-18 17:09:59 +000084 def randint(self, a, b):
Guido van Rossume7b146f2000-02-04 15:28:42 +000085 """Get a random integer in the range [a, b] including both end points.
86 (Deprecated; use randrange below.)"""
Guido van Rossum187f1541998-07-31 13:39:44 +000087 return self.randrange(a, b+1)
Guido van Rossume7b146f2000-02-04 15:28:42 +000088
Guido van Rossum2db91351992-10-18 17:09:59 +000089 def choice(self, seq):
Guido van Rossume7b146f2000-02-04 15:28:42 +000090 """Choose a random element from a non-empty sequence."""
Guido van Rossum2db91351992-10-18 17:09:59 +000091 return seq[int(self.random() * len(seq))]
Guido van Rossume7b146f2000-02-04 15:28:42 +000092
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."""
Guido van Rossum187f1541998-07-31 13:39:44 +000098 # 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
Guido van Rossumcc221c41998-08-10 20:07:53 +0000122 else:
123 raise ValueError, "zero step for randrange()"
Guido van Rossum187f1541998-07-31 13:39:44 +0000124
Guido van Rossumcc221c41998-08-10 20:07:53 +0000125 if n <= 0:
126 raise ValueError, "empty range for randrange()"
127 return istart + istep*int(self.random() * n)
Guido van Rossumc6360141990-10-13 19:23:40 +0000128
129
130# Initialize from the current time
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000131_inst = whrandom()
Guido van Rossum2db91351992-10-18 17:09:59 +0000132seed = _inst.seed
133random = _inst.random
134uniform = _inst.uniform
135randint = _inst.randint
136choice = _inst.choice
Guido van Rossum187f1541998-07-31 13:39:44 +0000137randrange = _inst.randrange