diff --git a/Lib/random.py b/Lib/random.py index 1e0dcc87ed..e3dd48aa11 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -300,9 +300,16 @@ class Random(_random.Random): x[i], x[j] = x[j], x[i] else: _int = int + _min = min for i in reversed(range(1, len(x))): - # pick an element in x[:i+1] with which to exchange x[i] - j = _int(random() * (i+1)) + # Pick an element in x[:i+1] with which to exchange x[i]. + # + # The min() clamp was added for Issue #24567 to handle + # a rare and platform dependent situation where j could + # rounded to an invalid index. Periodically, we should + # assess whether this clamp is still necessary since it + # obscures and slows down the code. + j = _min(i, _int(random() * (i+1))) x[i], x[j] = x[j], x[i] def sample(self, population, k): @@ -373,9 +380,16 @@ class Random(_random.Random): random = self.random if cum_weights is None: if weights is None: + # The min() clamp was added for Issue #24567 to handle + # a rare and platform dependent situation where j could + # rounded to an invalid index. Periodically, we should + # assess whether this clamp is still necessary since it + # obscures and slows down the code. _int = int + _min = min total = len(population) - return [population[_int(random() * total)] for i in range(k)] + return [population[_min(_int(random() * total), total)] + for i in range(k)] cum_weights = list(_itertools.accumulate(weights)) elif weights is not None: raise TypeError('Cannot specify both weights and cumulative weights') @@ -383,7 +397,14 @@ class Random(_random.Random): raise ValueError('The number of weights does not match the population') bisect = _bisect.bisect total = cum_weights[-1] - return [population[bisect(cum_weights, random() * total)] for i in range(k)] + hi = len(population) - 1 + # Clamping the bisect upper limit to *hi* was added for Issue + # #24567 to handle a rare and platform dependent situation where + # random()*total could slightly exceed the value of total. + # Periodically, we should assess whether this clamp is still + # necessary since it obscures and slows down the code. + return [population[bisect(cum_weights, random() * total, 0, hi)] + for i in range(k)] ## -------------------- real-valued distributions -------------------