连续抽卡出货的概率怎么算?抽数和出货个数之间的规律是怎样的?这篇文章就是用中学阶段的知识(顶多是数学竞赛初赛难度)来解答这些问题,很简单的哦~
和很多人编程模拟抽卡然后统计结果不一样,以下全部都是精确计算,而非模拟抽卡。
哈吉马路哟~简单易懂的抽卡数学~
只用单抽券的情形
定义
这里先忽略60%概率UP,把抽出四星就叫做“出货”。
同时也忽略十连抽卡的特殊概率,仅使用出货概率的单抽券。
用随机变量表示单抽次后得到的四星魔法少女的个数。
“单抽次出货个”这一事件便记作,该事件发生的概率记作。
那么当不变时,随机变量的数学期望就是。
例如:单抽1次出货1个的概率,出货0个的概率,因此单抽1次的出货个数的数学期望
简单易懂的切入角度
考虑最简单的情形,
由于两次单抽是独立随机事件事件,假如用和分别代表第一抽出货和不出货的概率、用和代表第二抽出货和不出货的概率的话,那么
这就是数列第二项与第一项之间的递推关系。
同理,数列第三项与前两项的递推关系就是
可以注意到,上式最后一项代表的是第三抽出了货的情形,倒数第二项代表的是第三抽没出货、第二抽出了货的情形,剩下的那项代表的是第三抽和第二抽都没出货的情形。
简单易懂的推广
因此,推广到101项的时候,可以同理写出
不过这里有一个小小的问题:连续99抽不出货之后,第100抽不出货也要出货。
上式的前面的项都包含有某个,对应了至少有一次出货的情形,唯独最后一项代表第2抽到第101抽均不出货,因此该项应修正为
然后用和来重写上式,就得到
将上式的替换成便可得到数列的阶线性递推关系:
不难发现上式的常数项,因此可以写成
求解数列通项公式
首先来去掉常数项:定义一个新的数列,其中是待定常系数,那么递推关系就成了
令递推关系中的常数项等于零,即
因此要消去常数项,只需令
(其中使用了关系式来化简)
然后我们写出齐次递推数列的特征方程:
点击此处尝试求解该方程,并发现方程的解近似于均匀分布在复平面单位圆上。
点击此处严格求该方程当、时的精确解,得到、、、四个解。
于是猜想特征方程的个复根应该就是,(),代入特征方程不难验证其成立。
有了特征根,可以直接看出()是种符合数列递推关系的解。
它们线性组合成数列递推关系的通解
其中是个待定常系数。
另一方面,数列的前100项是已知的:当即还没到保底时,每一抽的数学期望都是并且互相独立,因此
()
用这条等式求解个待定系数,可以算得
(其中使用了关系式来化简)
结论
单抽次后得到的四星魔法少女个数的数学期望是
代入的表达式则是:
代入、得到数值表达式:
假如要求的是pickup四星魔法少女的个数的数学期望,则需往上式再乘以
注意当超过100之后,上式第三项将迅速下降到不足0.01的程度,整体走势近似于线性增长:
只用十连券的情形
定义
先忽略60%概率UP,不过这次只使用十连券,当中包含一次出货概率的单抽和九次出货概率的单抽。
要完整描述不停十连的随机过程,我们要引入“状态”的概念。即便都是次十连后得到了个四星魔法少女,只要最后的保底计数不一样,那就是两个不同的状态。
用表示次十连后得到了个四星魔法少女、并且保底计数为的概率,且符合归一性:
那么次十连出货个数的数学期望就是
由于十连概率的复杂性,精确解出数学期望关于抽数的解析公式已经不实际,因此退而求其次用程序执行递推计算。
递推方式
通过一次十连从一个状态转变到另一个状态的过程可以用“转移矩阵”来描述,这个转移过程是和当前的十连抽数及已出货数量无关的,重要的是转移前后两个状态的保底计数和以及这次十连的出货数量。
用表示从保底计数经过一次十连出货个、变成保底计数的概率,,且有归一性:
那么所求概率的递推公式就是:
初始条件是:,
简单探讨一下
当然,在很多情况下,递推关系可以化简,例如假如保底计数,那么说明这次十连没有出货,因此只依赖于,并且等于它乘上十次失败的概率。这也就是说:
也可以换一个角度说,假如这次十连的出货数量,那么和必须满足,这一等式已经隐含了的条件,也隐含了的条件(即保底计数至少90的话这个十连必然出货才对)。这也就是说:
而对于这次十连的出货数量的时候,则需要区分出可能触发保底和没触发保底两种情况。
假如出货数量,而十连后的保底计数,这说明刚好在第十抽出了货。
- 假如旧的保底计数,则说明是提前出货的,必定没触发保底;
- 假如,那么它必定触发了保底,概率就是另外九抽都不出货的概率;
- 假如,这种情况是不存在的,即,因为这意味着除了第十抽出货以外还应当有一次保底出货才对。
稍微推广一下,在出货数量的情况下,十连后的保底计数必须满足,即,此时出货的恰好是第抽。
- 假如旧的保底计数,则说明是提前出货的,必定没触发保底;
- 假如,那么它必定触发了保底,概率就是另外九抽都不出货的概率;
- 假如,这种情况不存在,即,因为这意味着应当至少出货了两次才对。
类似的,假如出货数量,则十连后的保底计数必须满足,即,此时出货的是第抽和更之前的第抽()。
- 假如旧的保底计数,则说明第抽是提前出货的,必定没触发保底;
- 假如,那么就要计算两种情况的概率之和,一种是第抽提前出货,,按照没触发保底计算概率,另一种是第抽刚好触发保底,,按照另外九抽里面固定位置(第抽)出货一次来计算概率。
假如出货数量,则十连后的保底计数必须满足,即,此时出货的是第抽和更之前的某两抽,设第一次出货的是。
- 假如旧的保底计数,则说明第抽是提前出货的,必定没触发保底;
- 假如,那么就要计算两种情况的概率之和,一种是第抽提前出货,,按照没触发保底计算概率,另一种是第抽刚好触发保底,,按照另外九抽里面除了固定位置(第抽)以外还有一次位于第抽和第抽之间的另一抽也出货,这样两次出货来计算概率。
推而广之,对于出货数量,则十连后的保底计数必须满足,即,此时出货的是第抽和更之前的某抽,设第一次出货的是。
- 假如旧的保底计数,则说明第抽是提前出货的,必定没触发保底;
- 假如,那么就要计算两种情况的概率之和,一种是第抽提前出货,,按照没触发保底计算概率,另一种是第抽刚好触发保底,,按照另外九抽里面除了固定位置(第抽)以外还有次位于抽和第抽之间的抽也出货,这样次出货来计算概率。
递推公式结论
为了方便叙述,我们定义一个基础概率:
它代表10抽里面有固定位置的某抽出货、另外某抽没出货的概率。要计算没触发保底的概率,我们会用到其中的情况;要计算刚好触发保底的概率,我们会用到其中的情况。
那么前面的分类讨论就可以重新合并表述为:
- 当时,;
- 当时,首先,而对于的情形则分类讨论:
- 假如,则没触发保底,且在1个固定位置提前出货,;
- 假如,则刚好触发保底,另外九抽没出货,;
- 假如,则;
- 当时,首先,而对于的情形则分类讨论:
- 假如,则没触发保底,除了1个固定位置出货外,还有其前面个位置当中的个位置也出货,;
- 假如,那么位于第抽之前的那个出货抽的可能位置可以分成三种情况:
- 第一种情况是第抽刚好触发保底,其余的抽位于第抽和第抽之间,有个位置,情况数量是;
- 第二种情况是第抽的位置也没出货(虽然这不符合保底机制),全出货抽都位于它后面的个位置中,情况数量是;这两种情况数量之和;
- 第三种情况是在第保底抽的位置之前就出货了,三种情况数量之和恰恰是,因此单独第三种情况数量就是;
- 因此将触发保底的概率和没触发保底的概率加起来得到
用代码表述的话就是:
def M(j, m, n):
if j==0:
if m!=n-10: return 0
else: return B(0, 10)
elif j==1:
if n>=10: return 0
elif m<=89+n: return B(1, 9)
elif m==90+n: return B(0, 9)
else: return 0
else:
if n>=11-j: return 0
elif m<=88+j+n: return binom(9-n, j-1)*B(j, 10-j)
else: return binom(m-n-91, j-2)*B(j-1, 10-j) + (binom(9-n, j-1)-binom(m-n-90, j-1))*B(j, 10-j)
程序代码
# coding=utf-8
# gacha_probabilities_v4.py
import numpy as np
import math
print("start")
p = 0.01
q = 0.02
Number = 1000
kmax = math.ceil(Number/10)
B_full = np.zeros(shape=(11))
B_part = np.zeros(shape=(10))
M_bubaodi = np.zeros(shape=(11, 10))
M_baodi = np.zeros(shape=(11, 9, 9))
def B(i, j):
return (j/10)*(1-p)**(j-1)*(1-q)*p**i + (i/10)*(1-p)**j*p**(i-1)*q + (1-(i+j)/10)*(1-p)**j*p**i
for j in range(11):
B_full[j] = B(j, 10-j)
for j in range(10):
B_part[j] = B(j, 9-j)
for j in range(1, 11):
for n in range(0, 11-j):
M_bubaodi[j][n] = math.comb(9-n, j-1)*B_full[j]
for j in range(2, 11):
for n in range(0, 11-j):
for m in range(89+j+n, 100): # m>=89+j+n>=91
M_baodi[j][n][m-91] = math.comb(m-n-91, j-2)*B_part[j-1] + (math.comb(9-n, j-1)-math.comb(m-n-90, j-1))*B_full[j]
print("M matrix ready")
P = np.zeros(shape=(kmax+1, 10*kmax+1, 100))
P[0][0][0] = 1
P_check = np.zeros(shape=(kmax+1))
E = np.zeros(shape=(kmax+1))
for k in range(1, kmax+1):
for i in range(10*k+1):
for n in range(100):
if n>=10:
P[k][i][n] = P[k-1][i][n-10] * B_full[0] # case j==0
else:
if i==0: continue
for m in range(90+n):
P[k][i][n] += P[k-1][i-1][m] * B_full[1] # case j==1, m<=89+n
P[k][i][n] += P[k-1][i-1][90+n] * B_part[0] # case j==1, m==90+n
for j in range(2, min(i, 10-n)+1): # case j>=2
for m in range(89+j+n):
P[k][i][n] += P[k-1][i-j][m] * M_bubaodi[j][n] # case m<=88+j+n
for m in range(89+j+n, 100):
P[k][i][n] += P[k-1][i-j][m] * M_baodi[j][n][m-91] # case m>=89+j+n
P_check[k] += P[k][i][n]
E[k] += P[k][i][n]*i
print("k = %3g, E[k] = %3.10f, P_check = %3.10f" % (k, E[k], P_check[k]))
运行结果
执行了50次十连后手动暂停了程序,共500抽,图为每次十连后得到的四星魔法少女数量的数学期望。
考虑到浮点计算始终会有精度问题,计算误差会逐渐累积,所以每执行一步十连后都把此时所有可能情况的概率求和,以P_check
的值与1的差值来衡量计算误差的大小。
计算结果大体上是:
100抽时
200抽时
300抽时
400抽时
500抽时
此外请注意需要乘上才是得到的pickup四星魔法少女的个数的数学期望。
一般认为在430抽时根据数学期望就已经满孔了,但数学期望是会骗人的,“430抽时抽得pickup四星个数的数学期望是4个”和“抽满4个pickup四星所需的抽数的数学期望是430抽(或者别的数字)”是完全不同的两码事,更不要提那意味着430抽内抽满和430抽抽不满的概率基本上是一半一半,并不能保证你430抽就一定能抽满孔。