【问题标题】:making a weighted random state function extensible使加权随机状态函数可扩展
【发布时间】:2018-07-08 18:27:59
【问题描述】:

我有一个加权随机状态函数,它采用有限数量的相对权重并根据伪随机选择输出一个整数。但是,我想让函数可扩展以处理 n 个状态/权重。什么是重写这个函数的优雅方法,它可以将 *args 作为输入?

编辑:由于这些权重是相互关联的,所以对我来说棘手的部分是考虑使 elif 逻辑可扩展的最佳方法。

def weighted_random(weight1, weight2, weight3, weight4, weight5, weight6, weight7, weight8):
    totalWeight = weight1 + weight2 + weight3 + weight4 + weight5 + weight6 + weight7 + weight8
    randomInt = random.randint(1, totalWeight)

    if randomInt <= weight1:
        return 0
    elif randomInt > weight1 and randomInt <= (weight1 + weight2):
        return 1
    elif randomInt > (weight1 + weight2) and randomInt <= (weight1 + weight2 + weight3):
        return 2
    elif randomInt > (weight1 + weight2 + weight3) and randomInt <= (weight1 + weight2 + weight3 + weight4):
        return 3
    elif randomInt > (weight1 + weight2 + weight3 + weight4) and randomInt <= (weight1 + weight2 + weight3 + weight4 + weight5):
        return 4
    elif randomInt > (weight1 + weight2 + weight3 + weight4 + weight5) and randomInt <= (weight1 + weight2 + weight3 + weight4 + weight5 + weight6):
        return 5
    elif randomInt > (weight1 + weight2 + weight3 + weight4 + weight5 + weight6) and randomInt <= (weight1 + weight2 + weight3 + weight4 + weight5 + weight6 + weight7):
        return 6
    elif randomInt > (weight1 + weight2 + weight3 + weight4 + weight5 + weight6 + weight7):
        return 7
    else:
        return("error")

【问题讨论】:

  • 您的权重是否假定为排序整数?
  • @dawg 我认为它们是整数,但没有排序。像weighted_random(5, 1, 4) 这样的东西会给你 50% 的命中索引 0、索引 1 的 10% 和索引 2 的 40%。
  • @fafl:它的计算方式在数学上不支持这一点。
  • @dawg 嗯,我觉得不错。你有一个不起作用的例子吗?

标签: python function random


【解决方案1】:

您可以使用*args 将所有参数作为列表获取。然后您可以遍历它们以查找randomInt 中的哪一个。

def weighted_random(*weights):
    totalWeight = sum(weights)
    randomInt = random.randint(1, totalWeight)

    for i, weight in enumerate(weights):
        if randomInt <= weight:
            return i
        randomInt -= weight

    return "error"

如果这太易读或 O(n) 太快,那么试试这个:

def weighted_random(*weights):
    randomInt = random.randint(1, sum(weights))
    return next((
        i for i in range(len)
        if sum(weights[:i+1]) < randomInt <= sum(weights[:i+2])
    ), "error")

【讨论】:

  • 谢谢,但我认为棘手的部分是使 elif 语句的逻辑可扩展。由于这里的权重必须是相对的,我首先将所有权重相加,然后让随机整数落入与指定的整体匹配的条件语句中。我了解如何使用 for 循环以 Python 方式执行此操作。我没有得到一个非常漂亮的单线来做这个逻辑。
  • @DanielMiller 我不明白你为什么想要这个,但我添加了一些更短的内容
  • 甜蜜!谢谢。正如您所指出的,我可以看到您的第一个版本可能因其可读性而受到青睐。总是很高兴知道几种做某事的方法!
【解决方案2】:

如果您尝试创建一个使用累积权重的函数,您可以按照以下方式进行操作(假设 Python 3.6+):

import random 
import itertools as it

def wran(*weights):
    li=random.choices(list(range(len(weights))),cum_weights=list(it.accumulate(weights)))
    return li[0]

所以假设你想要[0,1,2,3,4,5] 的权重为[50,5,5,5,15,20](因为它们加起来等于 100 可以看作是百分比),你可以这样做:

from collections import Counter
>>> t=100000 
>>> [(k,'{:.6}%'.format(float(v)/t*100)) for k,v in sorted(Counter(wran(50,5,5,5,15,20) for _ in range(t)).items())]
[(0, '49.926%'), (1, '4.922%'), (2, '5.015%'), (3, '5.067%'), (4, '15.125%'), (5, '19.945%')]

这接近假设的百分比。


如果您想在 Python 3.6+ 以外的 Python 上执行此操作,您可以生成列表的累积总和的元组,然后使用 bisect 获取随机数适合该列表的索引元组:

import bisect
def wran(*weights):
    totalWeight = sum(weights)
    randomInt = random.randint(1, totalWeight)
    li=[(sum(weights[0:i]),sum(weights[0:i+1])) for i in range(len(weights))]
    return bisect.bisect_left(li,(randomInt,))-1

对此进行测试:

>>> [(k,'{:.6}%'.format(float(v)/t*100)) for k,v in sorted(Counter(wran(50,5,5,5,20,5,5,5) for _ in range(t)).items())]
[(0, '49.943%'), (1, '4.979%'), (2, '5.048%'), (3, '4.968%'), (4, '20.06%'), (5, '5.059%'), (6, '4.954%'), (7, '4.989%')]

与最初发布的功能相比:

>>> [(k,'{:.6}%'.format(float(v)/t*100)) for k,v in sorted(Counter(weighted_random(50,5,5,5,20,5,5,5) for _ in range(t)).items())]
[(0, '49.986%'), (1, '5.203%'), (2, '4.962%'), (3, '4.998%'), (4, '19.977%'), (5, '5.004%'), (6, '4.986%'), (7, '4.884%')]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 2016-01-10
    • 2014-08-30
    • 2013-10-26
    • 2018-03-15
    相关资源
    最近更新 更多