关于物品掉落概率的实现

768 阅读4分钟

关于物品掉落概率的实现

在概率的独立事件和连续事件区分不清时,物品掉落概率计算容易出现错误,所以做一个小节。

配置实现

在实际应用中,道具表设计可能如下:

id概率相对值物品名称等
11000道具1
21000道具2
31000道具3
4…………

这里概率使用万分比的整数表示法,称为概率相对值。即配置1000时,视为100010000 \frac{1000}{10000}。 使用这个表示法是出于几个目的:

  1. 万分比的精度满足绝大多数场合。每次抽取1/10000的概率触发,10连也就1/1000的概率。
  2. 使用概率相对值方便控制稀有度,比如普通道具可以一律使用1000表示10%,稀有道具一律使用1表示0.1%。当对整体概率有需求时,可以通过控制分母来实现。比如分母不再是10000,而是2000。此时普通道具掉落概率为50%,稀有道具为5%。
  3. 方便使用轮盘赌算法来处理概率。

轮盘赌

错误的方法

如果疏忽概率的基本计算方法,则可能面对概率损失问题。

在上面的例子中:
道具1的掉落概率为: 1000/10000
道具2的掉落概率为: (1 - 1000/10000)1000/10000,即道具1不掉落的概率道具2掉落的概率。
道具3的掉落概率为:道具1不掉落的概率道具2不掉落的概率道具3掉落的概率。

类似错误方法的代码实现(python):

def rand():
    times = 10000  # 循环x次

    x1 = 1000  # 道具1的掉落概率相对值
    x2 = 1000  # 道具2的掉落概率相对值
    x1Hit = 0  # 道具1的掉落次数
    x2Hit = 0  # 道具2的掉落次数
    denominator = 10000  # 分母

    i = 0
    while i < times:  # 模拟掉落x次
        # 判断道具1是否掉落
        x = randint(1, denominator)
        if x1 > x:  # 道具1 掉落
            x1Hit += 1  # 道具1的掉落次数+1
            i += 1
            continue

        # 判断道具2是否掉落
        x = randint(1, denominator)
        if x2 > x:  # 道具2 掉落
            x2Hit += 1  # 道具2的掉落次数+1
            i += 1
            continue

        i += 1
        

其结果: 总共判定掉落次数 10000
道具1的掉落次数 1018
道具2的掉落次数 879 <-明显不符合预期

如果采用遍历的方式多次判断概率,那么越后面的道具,要面临额外的概率损失。

结合配置实现的轮盘赌

轮盘赌是一个算法,常用于遗传算法中的选择等。有个小区别就是不需要归一化。

同样以上面例子,轮盘赌的代码实现(python):

def roulette():
    times = 10000  # 循环x次

    x1 = 1000  # 道具1的掉落概率相对值
    x2 = 1000  # 道具2的掉落概率相对值
    denominator = 10000  # 分母

    # 计算各个道具的掉落范围
    xArray = [x1, x2]  # 道具掉落概率数组
    hitArray = [0, 0]  # 道具掉落次数数组

    rangeRight = 0  # 右区间
    wheel = []  # 轮盘
    for v in xArray:  # 计算各个道具的掉落区间
        rangeRight += v
        wheel.append(rangeRight)

    print(wheel)

    # 判断掉落times次
    i = 0
    while i < times:
        x = randint(1, denominator)
        for k in range(len(wheel)):  # 按序逐个判断是否落在区间内
            if wheel[k] > x:  # 落在区间内
                hitArray[k] += 1  # 计数器+1
                break

        i += 1

其结果为:
总共判定掉落次数 10000
道具1的掉落次数 1007
道具2的掉落次数 973
符合预期。

简单的说,就是:

  • 第1个物品的掉落区间是:[1, 第一个物品的掉落概率]
  • 第2个物品的掉落区间是:[之前所有物品的掉落概率相对值总和, 之前所有物品的掉落概率相对值总和+第二个物品的掉落概率相对值]

用数轴表示为: 编组 6.png

至此,最基本的实现完成。

总结:用一次随机来判定概率,避免变成连续事件是处理概率损失的核心思路。

两种特殊的情况

必定掉落的情况

当分母等于最后一个物品的右区间,即可实现必定掉落一个物品。

使用上面的例子,即随机[1, 2000]

这种解决方法也适用于分母总和大于10000,又必定掉落一个物品时的情况。

分母总和大于10000,不希望出现必定掉落的情况

  1. 首先设定不掉落的概率相对值为x
  2. 其次设定最后一个物品的右区间为a
  3. 当预期的不掉落概率为p时,有:
xx+a=p\frac{x}{x+a}=p

上式等价于:

x=ap1px=\frac{ap}{1-p}

其中p为已知的设定值,所以x可求。

使用上面的例子,不设定为10000,而设定不掉落概率为50%时,有:
x = 2000 * 50% / (1-50%) = 2000。
即分母为2000+2000=4000。

总结:当需要对掉落情况进行额外设定时,可以通过调整分母来实现。