我会撕开choices implemention from the random module。就像是:
from random import SystemRandom
from itertools import accumulate as _accumulate, repeat as _repeat
from bisect import bisect as _bisect
def choices(population, weights, *, k=1):
randrange = SystemRandom().randrange
n = len(population)
cum_weights = list(_accumulate(weights))
if len(cum_weights) != n:
raise ValueError('The number of weights does not match the population')
total = cum_weights[-1]
if not isinstance(total, int):
raise ValueError('Weights must be integer values')
if total <= 0:
raise ValueError('Total of weights must be greater than zero')
bisect = _bisect
hi = n - 1
return [population[bisect(cum_weights, randrange(total), 0, hi)]
for i in _repeat(None, k)]
可以测试为:
from collections import Counter
draws = choices([1, 2, 3], [1, 2, 3], k=1_000_000)
print(dict(sorted(Counter(draws).items())))
给我:
{1: 166150, 2: 333614, 3: 500236}
看起来是对的。
更新:只是想检查一个错误,这里看起来不错:
print(
choices([1, 2, 3], [1, 0, 0], k=5),
choices([1, 2, 3], [0, 1, 0], k=5),
choices([1, 2, 3], [0, 0, 1], k=5),
)
给予:
[1, 1, 1, 1, 1] [2, 2, 2, 2, 2] [3, 3, 3, 3, 3]
这似乎也是对的。