【问题标题】:Cryptographically-secure, exactly-weighted sampling加密安全、精确加权的采样
【发布时间】:2022-11-28 05:46:57
【问题描述】:

在以下条件下,如何选择具有替换和权重的k元素?

  • 随机性必须是加密安全的,例如在secrets 模块中使用。
  • 加权必须精确,即使用整数而不是浮点运算。

自行编写的代码可能不如可用的实现安全和高效。据我所知,以下实现不符合我的要求。

【问题讨论】:

  • “权重必须精确”约束的意义何在?请注意,“整数”可能是比积分更好的术语,积分很容易与数学运算符混淆
  • 请注意,random.SystemRandom.choices 似乎满足第一个约束条件

标签: python python-3.x random


【解决方案1】:

我会撕开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]

这似乎也是对的。

【讨论】:

  • bisect 模块正是我所需要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-28
  • 2021-08-20
  • 2017-02-20
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 2011-02-05
相关资源
最近更新 更多