这篇文章是看到云风 云风的 BLOG: 10 连抽保底的概率模型 所想到的。
在云风的文章里面,这个10连抽保底模型最后转换为:
- 在两次抽取橙卡之间,可能要抽取N张白卡
- 为了做10连抽保底,N最大值是9.
- N服从指数分布
将[0.0, 1.0)之间均匀分布的随机数,转换成为指数分布的随机数,在 这里 可以找到答案:
- 假设我们有均匀分布随机数p, 然后希望转换成为目标分布f(x)的随机数
- 先求解出这个目标分布f(x)的CDF,也就是累积分布函数F(x),这个累积分布函数的值域是[0.0, 1.0)
- 然后令F(x) = p, x = F’(p), 其中F’就是F的逆函数
以指数分布为例,它的CDF(x) = (1 - e ^ (-lam * x)) (x >= 0). 令CDF(x) = p, 那么 x = ln(1-p) / (-lam). ️
然后问题就是如何选择这个lam. 指数分布的期望是E = 1 / lam. 比如lam = 0.1, E = 10,也就是平均需要抽取到10张白卡。 E(也就是lam的选择)直接影响到最终的分布,选择任意的E其实都没有问题(云风在文章里面选择rate=10) 基本上E很大(比如20)的话,那么0-9的分布就更均匀一些,E很小(比如5)的话,那么都会集中在小数字上。
np.random.seed(42)
def gen(E=9):
while True:
p = np.random.random()
x = int(np.log(1-p) * -E)
assert x >= 0
if x < 10:
break
return x
Es = [5,9,15,20]
df = pd.DataFrame()
for E in Es:
xs = pd.Series([gen(E) for i in range(10000)])
df['E={}'.format(E)] = xs
_ = df.hist(bins = 10, figsize=(20, 10))
但是无论如何,最终得到的分布肯定都不再是指数分布了,因为长尾没有了。粗略估计,如果真正要满足指数分布的话,那么应该给予N=9无穷大的概率,E应该是大约在4.5左右才符合指数分布。
让如何让最终分布满足指数分布呢?是否还有更简单的做法呢? 我觉得可以倒退:
- 首先分布还是指数分布
- N=9的时候必须给予很大的概率(这个概率不好估计,可以从0.99开始试)
- P(0) + .. P(9) = 1.0
def est_lam(x, p):
lam = - np.log(1-p) / x
return lam
lam = est_lam(9, 0.869) # 这个是多次测试的结果
print('E = {}, lam = {}'.format(1/lam, lam))
v = 0
for i in range(10):
p = lam * np.exp(-lam * i)
print('p({}) = {}'.format(i, p))
v += p
print(v)
""" Output
E = 4.427918020444273, lam = 0.22583977286455395
p(0) = 0.22583977286455395
p(1) = 0.18018534315870097
p(2) = 0.1437601423230733
p(3) = 0.11469844416006433
p(4) = 0.09151168662016494
p(5) = 0.07301222653361038
p(6) = 0.058252507633495834
p(7) = 0.046476526010727784
p(8) = 0.03708110702488957
p(9) = 0.029585010245256563
1.0004027665745376
"""
数据有这些含义:
- p(0) = 0.2258 表示抽取一张橙色牌之后,下次有22.58%概率立刻抽取到橙色牌
- E = 4.4279 表示抽取一张橙色牌之后,平均需要抽取4.4279张白色牌才能再次抽取橙色牌
- 橙牌:白牌比例是1: 4.4279 = 18.4%