Index: random.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/random.py,v retrieving revision 1.51.8.3 diff -c -r1.51.8.3 random.py *** random.py 8 Sep 2003 19:15:43 -0000 1.51.8.3 --- random.py 29 Sep 2003 15:49:51 -0000 *************** *** 53,58 **** --- 53,59 ---- TWOPI = 2.0*_pi LOG4 = _log(4.0) SG_MAGICCONST = 1.0 + _log(4.5) + BPR = 53 # Number of bits in a float # Translated by Guido van Rossum from C source provided by # Adrian Baddeley. Adapted by Raymond Hettinger for use with *************** *** 131,142 **** ## -------------------- integer methods ------------------- ! 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 --- 132,144 ---- ## -------------------- integer methods ------------------- ! def randrange(self, start, stop=None, step=1, int=int, default=None, ! maxwidth=1L< 0: + if istart >= maxwidth: + return self._bigrand(istart) return int(self.random() * istart) raise ValueError, "empty range for randrange()" *************** *** 153,159 **** istop = int(stop) if istop != stop: raise ValueError, "non-integer stop for randrange()" ! if step == 1 and istart < istop: # Note that # int(istart + self.random()*(istop - istart)) # instead would be incorrect. For example, consider istart --- 157,164 ---- istop = int(stop) if istop != stop: raise ValueError, "non-integer stop for randrange()" ! width = istop - istart ! if step == 1 and width > 0: # Note that # int(istart + self.random()*(istop - istart)) # instead would be incorrect. For example, consider istart *************** *** 166,172 **** # can return a long, and then randrange() would also return # a long, but we're supposed to return an int (for backward # compatibility). ! return int(istart + int(self.random()*(istop - istart))) if step == 1: raise ValueError, "empty range for randrange()" --- 171,179 ---- # can return a long, and then randrange() would also return # a long, but we're supposed to return an int (for backward # compatibility). ! if width >= maxwidth: ! return istart + self._bigrand(width) ! return int(istart + int(self.random()*width)) if step == 1: raise ValueError, "empty range for randrange()" *************** *** 175,188 **** 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) def randint(self, a, b): --- 182,198 ---- if istep != step: raise ValueError, "non-integer step for randrange()" if istep > 0: ! n = (width + istep - 1) / istep elif istep < 0: ! n = (width + istep + 1) / istep else: raise ValueError, "zero step for randrange()" if n <= 0: raise ValueError, "empty range for randrange()" + + if n >= maxwidth: + return istart + self._bigrand(n) return istart + istep*int(self.random() * n) def randint(self, a, b): *************** *** 190,195 **** --- 200,231 ---- """ return self.randrange(a, b+1) + + def _bigrand(self, n, int=int, bpr=BPR, maxwidth=1L< 0 and n == int(n) + random = self.random + cumrnd = 0L + cumrng = 1L + while cumrng <= n: + cumrng <<= bpr + cumrnd <<= bpr + cumrnd |= long(random() * maxwidth) + + # Avoid bias by limiting the range to an exact multiple of n. + # This assures each [0,n) corresponds to an equal number of + # possibilities in the range: 0 <= cumrnd < limit. + scale, rem = divmod(cumrng, n) + limit = cumrng - rem + if cumrnd >= limit: + return self._bigrand(n) + assert 0 <= cumrnd < n*scale == limit <= limit+rem == cumrng + return cumrnd // scale ## -------------------- sequence methods ------------------- Index: test/test_random.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_random.py,v retrieving revision 1.12.8.2 diff -c -r1.12.8.2 test_random.py *** test/test_random.py 5 Sep 2003 21:40:30 -0000 1.12.8.2 --- test/test_random.py 29 Sep 2003 15:49:51 -0000 *************** *** 220,225 **** --- 220,242 ---- seed = (1L << (10000 * 8)) - 1 # about 10K bytes self.gen.seed(seed) + def test_53_bits_per_float(self): + # This should pass whenever a C double has 53 bit precision. + span = 2 ** 53 + cum = 0 + for i in xrange(50): + cum |= int(self.gen.random() * span) + self.assertEqual(cum, span-1) + + def test_bigrand(self): + # The randrange routine should build-up the required number of bits + # in stages so that all bit positions are active. + span = 2 ** 500 + cum = 0 + for i in xrange(50): + cum |= self.gen.randrange(span) + self.assertEqual(cum, span-1) + _gammacoeff = (0.9999999999995183, 676.5203681218835, -1259.139216722289, 771.3234287757674, -176.6150291498386, 12.50734324009056, -0.1385710331296526, 0.9934937113930748e-05, 0.1659470187408462e-06)