Severin 的解决方案很简单,对于大部分参数空间来说应该很快,但在分布的边缘可能会变慢。例如,生成 30 个总和为 100 的值可能需要几秒钟,直到它随机偶然发现一个有效的解决方案
以下代码确保它仅从有效值中采样,因此具有更具确定性的运行时:
def sample_values(population, k, total, *, weights=None):
if weights is None:
# weights not probabilities, so no need to sum to 1
weights = [1] * len(population)
# ensure population is a sorted list, with weights in consistant order
population, weights = zip(*sorted(zip(population, weights)))
population = list(population)
weights = list(weights)
result = []
for _ in range(k):
# population values that would take us past the running total should be excluded
while population[-1] > total:
del population[-1]
del weights[-1]
# maintain k as the number of remaining items
k -= 1
# remove anything where just using it and then maximal values wouldn't get us to the total
remain_lim = total - max(population) * k
while population[0] < remain_lim:
del population[0]
del weights[0]
# sample next value
n, = choices(population, weights)
result.append(n)
# maintain total as the remaining total
total -= n
return result
对tee 的第一次调用确实想要strict=True from Python 3.10,但我认为你还没有使用它,所以把它省略了
以上可以,例如,用作:
sample_values(range(5), 8, 12, weights=[0.20, 0.30, 0.30, 0.15, 0.05])
运行时间约为 12µs,与 Severin 的 multinomial 解决方案相媲美,这些参数需要 ~18µs。