summaryrefslogtreecommitdiffstats
path: root/Lib/whrandom.py
blob: 7f8fc60fab18268f1bb359dc6991cb159b99e362 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#	WICHMANN-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.


#	Translated by Guido van Rossum from C source provided by
#	Adrian Baddeley.


# 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.)


class whrandom:
	#
	# Initialize an instance.
	# Without arguments, initialize from current time.
	# With arguments (x, y, z), initialize from them.
	#
	def __init__(self, x = 0, y = 0, z = 0):
		self.seed(x, y, z)
	#
	# Set the seed from (x, y, z).
	# These must be integers in the range [0, 256).
	#
	def seed(self, x = 0, y = 0, z = 0):
		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)
	#
	# Get the next random number in the range [0.0, 1.0).
	#
	def random(self):
		# 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
	#
	# Get a random number in the range [a, b).
	#
	def uniform(self, a, b):
		return a + (b-a) * self.random()
	#
	# Get a random integer in the range [a, b] including both end points.
	# (Deprecated; use randrange below.)
	#
	def randint(self, a, b):
		return self.randrange(a, b+1)
	#
	# Choose a random element from a non-empty sequence.
	#
	def choice(self, seq):
		return seq[int(self.random() * len(seq))]
	#
	# Choose a random item from range([start,] step[, stop]).
	# This fixes the problem with randint() which includes the
	# endpoint; in Python this is usually not what you want.
	#
	def randrange(self, start, stop=None, step=1,
		      # Do not supply the following arguments
		      int=int, default=None):
		# 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