如何实现轮盘赌轮在python中挑选随机对象?

时间:2016-01-09 19:02:20

标签: python random probability

在python中,我有一个对象列表(列表的大小是> = 2),它有一个名为score的属性,其值为任何浮点数> = 0(无上限) )。

我想从列表中随机选择2个不同的对象,但要确保选择具有更高分数的值的可能性更高。基本上得分越高,被选中的机会就越大。我正在考虑做一个轮盘赌风格,在那里我得到所有分数的总和,然后得到每个项目的完美,其百分比是它的分数除以总分。

但是我如何仍然选择2个对象?

由于

5 个答案:

答案 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)}")