在python中,我有一个对象列表(列表的大小是> = 2),它有一个名为score
的属性,其值为任何浮点数> = 0(无上限) )。
我想从列表中随机选择2个不同的对象,但要确保选择具有更高分数的值的可能性更高。基本上得分越高,被选中的机会就越大。我正在考虑做一个轮盘赌风格,在那里我得到所有分数的总和,然后得到每个项目的完美,其百分比是它的分数除以总分。
但是我如何仍然选择2个对象?
由于
答案 0 :(得分:1)
问题不是很明确,但我想我明白你的意思。 您应该使用反向CDF类型方法。下面的示例将为您返回分数列表的索引,然后使用它来获取所需的值。显然,从编程的角度来看,有更聪明的方法可以做到这一点,但你显然需要理解这个方法,我认为这个例子对我们有很大帮助。
>>> import numpy as np
>>> import pandas as pd
>>> scores = [100, 200, 300, 400, 500]
>>> scores = np.array(scores)
>>> sum = scores.sum()
>>> sum
1500
>>> percentages = scores/float(sum)
>>> percentages
array([ 0.06666667, 0.13333333, 0.2 , 0.26666667, 0.33333333])
>>> cdf = percentages.cumsum()
>>> cdf
array([ 0.06666667, 0.2 , 0.4 , 0.66666667, 1. ])
>>> cdf = np.concatenate([np.array([0]), cdf])
>>> cdf
array([ 0. , 0.06666667, 0.2 , 0.4 , 0.66666667, 1. ])
>>>def returnobj(cdf, randnum):
>>> for i in range(len(cdf)-1):
>>> if((randnum>cdf[i])&(randnum<=cdf[i+1])):
>>> return i
##########
#This Bit is just to show how the results look, and to show it favours higher scores
>>>for i in range(10000):
>>> results.append(returnobj(cdf, np.random.random()))
>>>results=pd.DataFrame(results)
>>>results[results.columns[0]] = results[results.columns[0]].astype(str)
>>>results['a'] = results[0]
>>>print results.groupby(0).count()
0 639
1 1375
2 2039
3 2678
4 3269
答案 1 :(得分:0)
我使用静态(类)变量来跟踪单个对象的最大分数。然后我会有一个非静态方法来评估实例得分与最高得分的百分比。 (这基本上就是你所说的)
在我的主程序中,我会使用普通randrange
来选择一个对象的index
。然后我调用该对象的百分比函数并在0-1之间创建另一个随机变量。如果变量大于函数的结果选择对象,否则不要选择它。重复此操作,直到您选择了所需数量的对象。
这很可能不是最好和最灵活的方法,但它应该给你所需的比例。
答案 2 :(得分:0)
我会假设某个项目的分数没有变化,而且这些项目是唯一的。
获得随机加权项的最佳方法是索引累积权重列表。
要获取样本,只需继续挑选随机项,直到您有足够的唯一值。
代码:
from bisect import bisect_left
from itertools import accumulate
from random import random, shuffle
class WeightedRandom:
def __init__(self, items, weights):
self.items = list(items)
self.weights = list(weights)
self.cum_wt = list(accumulate(weights))
self.total_wt = self.cum_wt[-1]
def random(self):
value = random() * self.total_wt
index = bisect_left(self.cum_wt, value)
return self.items[index]
def sample(self, n):
items = set()
while len(items) < n:
items.add(self.random())
# Note: casting from set to list introduces
# an ordering bias; we have to remove this bias.
items = list(items)
shuffle(items)
return items
然后
>>> roulette = WeightedRandom("ABCDEFGHIJ", range(1, 11))
>>> for i in range(10):
... print(roulette.sample(2))
['J', 'D']
['J', 'C']
['I', 'F']
['C', 'H']
['J', 'E']
['A', 'G']
['J', 'H']
['E', 'I']
['I', 'D']
['E', 'I']
但请注意,如果第一个(n - 1)项与sample
的很大一部分相加,则total_wt
可能需要很长时间才能运行!
答案 3 :(得分:0)
这是一个“简单”的代码,可以执行我认为您想要的内容。
诀窍是选择一个,将其从列表中删除,然后重试。
此解决方案适用于您想要挑选的任意数量的项目......
import random
class Obj(object):
def __init__(self, score):
self.score = score
def __repr__(self):
return "<Obj %.2f>"%self.score
def get_items(l, num=2):
if num==0: return []
total = sum(o.score for o in l)
for obj in l:
obj._chance = obj.score / total
l = sorted(l, key=lambda o: o._chance)
selected = l[0] #float problems resolved ;)
r = random.random()
chance = 0.0
for o in l:
chance += o._chance
if chance > r:
selected = o
break
l.remove(selected)
return [selected] + get_items(l, num-1)
l = [Obj(1.0),Obj(2.0),Obj(3.0)]
print (get_items(l, num=2))
答案 4 :(得分:0)
使用像 numpy 的 tensorflow 这样的库有几种方法可以做到这一点:
tf.random.categorical([tf.math.log(probs)], 1).numpy()[0][0]
或:
np.random.multinomial(1, probs/sum_probs).argmax()
但是使用 random 模块的简单函数应该可以解决问题:
def roulette_wheel(re: random.Random, probs: List[float], sum_probs: float):
"""Returns the index of chosen element, with probability proportionate to its value"""
n = re.uniform(0, sum_probs)
for i, p in enumerate(probs):
if n <= p:
return i
n -= p
raise ValueError(f"{sum_probs} should be equal to {sum(probs)}")