关于物品掉落概率的实现
在概率的独立事件和连续事件区分不清时,物品掉落概率计算容易出现错误,所以做一个小节。
配置实现
在实际应用中,道具表设计可能如下:
| id | 概率相对值 | 物品名称等 |
|---|---|---|
| 1 | 1000 | 道具1 |
| 2 | 1000 | 道具2 |
| 3 | 1000 | 道具3 |
| 4 | …… | …… |
这里概率使用万分比的整数表示法,称为概率相对值。即配置1000时,视为。 使用这个表示法是出于几个目的:
- 万分比的精度满足绝大多数场合。每次抽取1/10000的概率触发,10连也就1/1000的概率。
- 使用概率相对值方便控制稀有度,比如普通道具可以一律使用1000表示10%,稀有道具一律使用1表示0.1%。当对整体概率有需求时,可以通过控制分母来实现。比如分母不再是10000,而是2000。此时普通道具掉落概率为50%,稀有道具为5%。
- 方便使用轮盘赌算法来处理概率。
轮盘赌
错误的方法
如果疏忽概率的基本计算方法,则可能面对概率损失问题。
在上面的例子中:
道具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个物品的掉落区间是:[之前所有物品的掉落概率相对值总和, 之前所有物品的掉落概率相对值总和+第二个物品的掉落概率相对值]
用数轴表示为:
至此,最基本的实现完成。
总结:用一次随机来判定概率,避免变成连续事件是处理概率损失的核心思路。
两种特殊的情况
必定掉落的情况
当分母等于最后一个物品的右区间,即可实现必定掉落一个物品。
使用上面的例子,即随机[1, 2000]
这种解决方法也适用于分母总和大于10000,又必定掉落一个物品时的情况。
分母总和大于10000,不希望出现必定掉落的情况
- 首先设定不掉落的概率相对值为x
- 其次设定最后一个物品的右区间为a
- 当预期的不掉落概率为p时,有:
上式等价于:
其中p为已知的设定值,所以x可求。
使用上面的例子,不设定为10000,而设定不掉落概率为50%时,有:
x = 2000 * 50% / (1-50%) = 2000。
即分母为2000+2000=4000。
总结:当需要对掉落情况进行额外设定时,可以通过调整分母来实现。