我在python中有一个字典,其key->值为str->int
。如果我必须根据它自己的值选择一个键,那么当值变大时,键的选择可能性就会降低。
例如,如果key1=2
和key2->1
,则key1
的态度应为2:1
。
我该怎么做?
答案 0 :(得分:2)
如果值对于gnibler的方法来说太大了:
构建元组(key, index)
的列表,其中index
是列表中键之前的所有值的总和(这将是第一次出现key
gnibler的索引列表c
。还计算所有值的总和(n
)。
现在,在0和x
之间生成一个随机数n - 1
。使用index < x
查找列表中的最后一个条目。由于列表是按索引排序的,因此您可以使用二进制搜索来有效地执行此操作。
更新:KennyTM的代码是一个实现,除了他使用强力线性搜索而不是二分搜索;如果密钥数量很大,这将是低效的。
答案 1 :(得分:1)
值不是太大,你可以这样做
>>> 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
答案 2 :(得分:1)
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 :(得分:0)
来自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