【问题标题】:Give lower values a higher weight in a `randint()` function在 randint() 函数中赋予较低的值较高的权重
【发布时间】:2021-10-05 02:43:22
【问题描述】:

使用randint(),我如何为较低的值赋予较高的权重(被选中的机会较高)?

我有以下代码:

def grab_int():
    current = randint(0,10) 
    # I want the weight of the numbers to go down the higher they get (so I will get more 0s than 1s, more 1s than 2, etc)

OBS!

我想以一种比其他答案更优雅的方式来做这件事(例如:Python Weighted Random)。有没有办法通过导入某种重量module来做到这一点?

规格: 我想要一个randint(),它返回0 的机会是30%,然后它会线性下降到10,有1% 的机会。

【问题讨论】:

标签: python random


【解决方案1】:

以下方法满足您的要求。它使用拒绝采样方法:随机均匀地生成一个整数,并以与其权重成正比的概率接受它。如果该号码不被接受,我们拒绝它并重试(另请参阅this answer of mine)。

import random

def weighted_random(mn, mx, mnweight, mxweight):
 while True:
    # Get the highest weight.
    highestweight=max(mnweight,mxweight)
    # Generate a uniform random integer in the interval [mn, mx].
    r=random.randint(mn,mx)
    # Calculate the weight for this integer.  This ensures the min's
    # weight is mnweight and the max's weight is mxweight
    weight=mnweight+(mxweight-mnweight)*((i-mn)/(mx-mn))
    # Generate a random value between 0 and the highest weight
    v=random.random()*highestweight
    # Is it less than this weight?
    if v<weight:
       # Yes, so return it
       return r
    # No, so try again

(诚然,由于浮点除法以及输出浮点数的random.random(),实现并不完全“优雅”,但下面的示例是,一旦我们编写了它。实现可以通过在fractions 模块中使用Fractions 来改进。)

这个方法也可以使用Python中已有的random.choices方法实现如下。首先我们计算random.choices 所需的权重,然后我们传递这些权重。但是,如果最小值和最大值之间的范围非常大,这种方法的效率并不高。

import random

# Calculate weights for `random.choices`
def make_weights(mn, mx, mnweight, mxweight):
 r=(mx-mn)
 return [mnweight+(mxweight-mnweight)*(i/r) for i in range(mn, mx+1)]

def weighted_random(mn, mx, mnweight, mxweight):
 weights=make_weights(mn, mx, mnweight, mxweight)
 return random.choices(range(mn, mx+1), weights=weights)[0]

使用 NumPy 库,这甚至可以按如下方式实现:

import random
import numpy

def weighted_random(mn, mx, mnweight, mxweight):
 return random.choices(range(mn, mx+1), \
      weights=numpy.linspace(mnweight,mxweight,(mx-mn)+1))[0]

weighted_random 函数的示例如下:

# Generate 100 random integers in the interval [0, 10],
# where 0 is assigned the weight 30 and 10 is assigned the
# weight 10 and numbers in between are assigned
# decreasing weights.
print([weighted_random(0,10,30,10) for i in range(100)])

【讨论】:

    【解决方案2】:

    我发现最简单的方法是手动分配权重:

    def grab_int():
        global percent
        global percentLeft
        global upby
    
        # I want the weight of the numbers to go down the higher they get (so I will get more 0s than 1s, more 1s than 2, etc)
        current = randint(0,100)
        if current < 30:
            upby = randint(1,2)
            #0
        elif current < 40:
            upby = 1
            #1
        elif current < 45:
            upby = 2
            #2
        elif current < 50:
            upby = 3
            #3
            upby = 4
            #4
        elif current < 60:
            upby = 5
            #5
        elif current < 65:
            upby = 6
            #6
        elif current < 70:
            upby = 7
            #7
        elif current < 75:
            upby = 8
            #8
        elif current < 90:
            upby = 9
            #9
        elif current < 95:
            upby = 10
            #10
        else: # I'm dumb so I accidentally only added up to 95%, This just gives 0 a 5% higher chance without having to rewrite all the other values
            upby = 0
            #0
    

    【讨论】:

    • 您能解释一下为什么您决定使用这个解决方案而不是我的解决方案吗?是因为您更关心“赋予较低的值较高的权重”而不是权重的确切分布吗?
    • @PeterO。因为我是 1 年级的编程学生,所以我一生都无法理解您提供的代码示例。我很高兴你把时间花在这上面,但是我无法理解你的代码。所以就看我哥了! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    • 1970-01-01
    • 2021-06-30
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多