【问题标题】:Generate random weights numbers with limited sum生成总和有限的随机权重数
【发布时间】:2021-04-13 19:21:56
【问题描述】:

我需要生成一个包含 [0, 4] 之间的 8 个具有权重的整数的随机列表,总和应为 12。

类似这样的:

from random import choices

while True:
    lst = choices(population=[0, 1, 2, 3, 4], weights=[0.20, 0.30, 0.30, 0.15, 0.05], k=8)
    if sum(lst) == 12:
        print(lst)
        break

有更聪明的方法吗?

【问题讨论】:

  • @dfundako 哪个函数?

标签: python random


【解决方案1】:

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。

【讨论】:

    【解决方案2】:

    您可以从 multinomial 中采样,它会自动获得正确的总和,并拒绝超出范围的值

    顺其自然,Python 3.9.1,Windows 10 x64

    import numpy as np
    
    rng = np.random.default_rng()
    
    def smpl(rng):
    
        while True:
    
            q = rng.multinomial(12, [1./8.]*8)
    
            if np.any(q > 4):
                continue
            return q
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-04
      • 2013-05-28
      • 2013-11-21
      • 1970-01-01
      • 2013-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多