【问题标题】:Sampling keys due to their values根据值对键进行采样
【发布时间】:2010-02-21 09:52:19
【问题描述】:

我在 python 中有一个字典,其 key->value 为str->int。如果我必须根据自己的值选择一个键,那么随着值变大,该键被选择的可能性就会降低。

比如key1=2和key2->1,那么key1的态度应该是2:1。

我该怎么做?

【问题讨论】:

标签: python dictionary sampling


【解决方案1】:

如果值对于 gnibler 的方法来说太大了:

构建一个元组列表(key, index),其中index 是列表中键之前的所有值的总和(这将是第一次出现key gnibler 的列表c 的索引。此外计算所有值的总和 (n)。

现在,生成一个介于 0 和 n - 1 之间的随机数 x。使用index < x 查找列表中的最后一个条目。由于列表是按索引排序的,因此您可以使用二分搜索来有效地做到这一点。

更新: KennyTM 的代码是这个的实现,除了他使用蛮力线性搜索而不是二分搜索;如果键的数量很大,这将是低效的。

【讨论】:

  • +1。这被称为轮盘赌选择或健身比例选择,通常用于遗传算法。
【解决方案2】:

1. 像这样构造一个类似 CDF 的列表:

def build_cdf(distrib):
    cdf = []
    val = 0
    for key, freq in distrib.items():
        val += freq
        cdf.append((val, key))
    return (val, cdf)

这个函数返回一个元组,第一个值是概率之和,第二个值是CDF。

2. 像这样构造采样器:

import random
def sample_from_cdf(val_and_cdf):
    (val, cdf) = val_and_cdf;
    rand = random.uniform(0, val)
    # use bisect.bisect_left to reduce search time from O(n) to O(log n).
    return [key for index, key in cdf if index > rand][0]

用法:

x = build_cdf({"a":0.2, "b":0.3, "c":0.5});
y = [sample_from_cdf(x) for i in range(0,100000)];
print (len([t for t in y if t == "a"]))   # 19864
print (len([t for t in y if t == "b"]))   # 29760
print (len([t for t in y if t == "c"]))   # 50376

你可能想把它变成一个类。

【讨论】:

    【解决方案3】:

    如果值不是太大,你可以这样做

    >>> from random import choice
    >>> d={"key1":2,"key2":1}
    >>> c=[]
    >>> for k,v in d.items():
    ...  c+=[k]*v
    ... 
    >>> choice(c)
    'key1'
    >>> sum(1 for x in range(100) if choice(c)=="key1")
    63
    >>> sum(1 for x in range(100) if choice(c)=="key2")
    36
    

    【讨论】:

      【解决方案4】:

      oefe 和 KennyTM 答案中算法的快速简单版本:

      def select_weighted(d):
         offset = random.randint(0, sum(d.itervalues())-1)
         for k, v in d.iteritems():
            if offset < v:
               return k
            offset -= v
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-26
        • 2016-01-10
        • 1970-01-01
        • 2012-03-10
        • 1970-01-01
        • 2019-03-19
        • 2022-07-26
        相关资源
        最近更新 更多