Index: Modules/_randommodule.c =================================================================== --- Modules/_randommodule.c (revision 82067) +++ Modules/_randommodule.c (working copy) @@ -410,7 +410,88 @@ return result; } +/* Use genrand_int32 to generate a random integer from range(k). The argument + k should satisfy 0 < k < 2**32. */ + static PyObject * +random__smallrandbelow(RandomObject *self, PyObject *objk) +{ + PY_LONG_LONG prod; + unsigned long k, candidate, remainder, boundary, top; + + k = PyLong_AsUnsignedLong(objk); + if (k == (unsigned long)-1 && PyErr_Occurred()) + return NULL; + if (k == 0UL || k > 0xffffffffUL) { + PyErr_SetString(PyExc_OverflowError, + "Argument to _smallrandbelow should " + "be in the range (0, 2**32)."); + return NULL; + } + + /* Conceptually: let X be a real-valued random variable, uniformly + * distributed on [0, 1). Then floor(k * X) gives a random integer + * uniformly distributed in range(k). + * + * Computationally: we do exactly the above, generating X one base-2**32 + * 'digit' at a time using genrand_int32, and using only as many digits of + * X as are necessary to uniquely determine the value of floor(k * X). For + * small k, we'll usually only need one digit. Generation typically takes + * one 32-by-32 to 64-bit multiplication, a subtraction, and a + * (predictable) comparison. + * + * Details: write X = (d + Y) / 2**32, where d is a random integer from + * range(2**32) (produced by a call to genrand_int32) and Y is again a + * real-valued random variable uniformly distributed on [0, 1). Let k * d = + * c * 2**32 + r, 0 <= r < 2**32 (or in other words, c and r are the high + * and low words of the 64-bit product k*d). Then + * + * floor(k * X) == c + floor((r + k * Y) / 2**32) + * + * Now floor((r + k * Y) / 2**32) is either 0 or 1; so we want to return + * either c or c + 1. To determine which, we only need to know whether + * + * r + k * Y >= 2**32 + * + * or equivalently, whether + * + * floor(k * Y) > 2**32 - 1 - r + * + * Expand again: write Y = (e + Z) / 2**32, with e obtained from genrand_int32 + * and Z uniform on [0, 1). Letting k * e == q * 2**32 + s, we get: + * + * floor(k * Y) = q + floor((s + k * Z) / 2**32). + * + * Again, the RHS is either q or q + 1, depending on the value of Z. So + * the only case where we don't know whether floor(k * Y) > 2**32 - 1 - r + * is when q == 2**32 - 1 - r. In this case, we repeat the computation, to + * determine whether s + k * Z >= 2**32, and we continue to loop until we + * get a definite result. + */ + + prod = (PY_LONG_LONG)genrand_int32(self) * k; + candidate = prod >> 32; + remainder = prod & 0xffffffffUL; + + while (1) { + boundary = 0xffffffffUL - remainder; + /* The next statement is pure optimization: if k <= boundary then it's + guaranteed that top < k <= boundary, so there's no need for another + call to genrand_int32. For small k, we almost always exit here. */ + if (k <= boundary) + break; + prod = (PY_LONG_LONG)genrand_int32(self) * k; + top = prod >> 32; + remainder = prod & 0xffffffffUL; + if (top != boundary) { + candidate = top < boundary ? candidate : candidate + 1; + break; + } + } + return PyLong_FromUnsignedLong(candidate); +} + +static PyObject * random_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { RandomObject *self; @@ -443,6 +524,8 @@ {"getrandbits", (PyCFunction)random_getrandbits, METH_VARARGS, PyDoc_STR("getrandbits(k) -> x. Generates a long int with " "k random bits.")}, + {"_smallrandbelow", (PyCFunction)random__smallrandbelow, METH_O, + PyDoc_STR("randrange(k) for k satisfying 0 < k < 2**32")}, {NULL, NULL} /* sentinel */ };