【幻塔】[数据研究] 意志抽取概率解析

更新时间2023/1/9287 浏览综合
原帖是大佬发布在NGA的攻略贴,个人在使用过程中受益颇多,申请搬运到taptap供大家使用。不做任何商业用途,如有侵权,我会立即删除本帖。
[数据研究] 意志抽取概率解析
我并没有玩过幻塔,所以信息都靠网络收集和zju幻塔群友提供帮助,非常感谢zju幻塔群友的帮忙
本贴很可能有些错漏,如有发现请指出!非常感谢!
===采用模型===
限定意志池中,获取SSR意志的基础概率为1.7%,当抽取次数为40的整数倍时,获取SSR意志的概率为100%。获取SSR意志时有50%概率为当期UP的SSR意志。每40抽的SSR保底在卡池轮换时仍会保留。
意志池中每抽一次可以获得对应超频晶片,每集满80个对应超频晶片可以兑换当期4个限定SSR意志中的一个。但需要注意的是,卡池轮换后超频晶片会转化为原型晶片无法保留。
特别的,每次限定意志池可以用50个当期武器池的回火铸金兑换1个指定意志(限一个)
===获取一个SSR意志期望===
按照 1-39抽 概率固定为1.7% 第40抽固定为100% 计算,综合概率为4.1575%,期望为24.053抽,在有效数字上说得通,由于没有可靠的统计数字支撑,那么姑且采用这样的模型。
简单计算可得包含80晶片兑换的情况下,获取一个限定SSR意志的期望为30.04抽,当然这个值不适合用来参考,毕竟集齐问题是另一回事了,还是要看集齐的分布是啥样。
===抽取分位函数图===
计算方法和实现
通过DP进行计算,定义C[当前状态]为第i抽时处于当前意志抽取状态的概率(不含兑换),通过简单递推计算C[当前状态]。需要注意的是为了防止状态爆炸,以抽满为封顶状态。(这里的DP可以优化,因为只关心是否集齐,不关心具体是哪个,一共n种物品,每个要抽k个的状态可以压缩到 C_{n+k}^{k},但懒得写了,反正问题规模小快不了多少)。
DP结束后,遍历所有状态根据是否兑换计算出 cdf,然后普通的画图,整个流程结束。
下面是计算和绘图代码,GGanalysis包见我的Github项目
[code=py]
import numpy as np
import GGanalysis as gg
def DP_state_distribution(max_pull=1000, item_rank=4):
# 返回最高抽 item_rank 对意志,最高 max_pull 下的状态分布
# C存的是状态持续 即第i抽时处于此状态的概率
C = np.zeros((max_pull+1, item_rank+1, item_rank+1, item_rank+1, item_rank+1))
C[0, 0, 0, 0, 0] = 1
base_p = 0.017
count_pity = 40
up_rate = 0.5
item_types = 4
for i in range(1, max_pull+1):
now_p = 1*up_rate if i%count_pity==0 else base_p*up_rate
for a in range(item_rank+1):
for b in range(item_rank+1):
for c in range(item_rank+1):
for d in range(item_rank+1):
# 普通递推
if a-1 >= 0:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a-1, b, c, d]
if b-1 >= 0:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b-1, c, d]
if c-1 >= 0:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b, c-1, d]
if d-1 >= 0:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b, c, d-1]
# 道具获取到设定数量上限情况 再获得更多状态压缩在一起
if a == item_rank:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b, c, d]
if b == item_rank:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b, c, d]
if c == item_rank:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b, c, d]
if d == item_rank:
C[i, a, b, c, d] += now_p / item_types * C[i-1, a, b, c, d]
# 没有抽到的情况
C[i, a, b, c, d] += (1-now_p) * C[i-1, a, b, c, d]
return C
def relu(x):
if x>0:
return x
return 0
def calc_cdf(C, need_rank=1, allow_exchange=True, use_weapon_50=0):
# C 为得到的状态分布 need_rank 为需要集齐多少组 allow_exchange 表示是否使用兑换 use_weapon_50表示是否使用50武器池铸金进行兑换
dist_len = C.shape[0]
dist_rank = C.shape[1]
cdf = np.zeros((dist_len))
for i in range(1, dist_len):
for a in range(dist_rank):
for b in range(dist_rank):
for c in range(dist_rank):
for d in range(dist_rank):
# 计算缺口
lack = relu(need_rank-a) + relu(need_rank-b) + relu(need_rank-c) + relu(need_rank-d)
# 可以依靠道具兑换补齐
if allow_exchange:
if lack 1e-20:
# temp = temp[:j+1]
# break
# 转化为 gg 的格式
return gg.FiniteDist(temp)
C = DP_state_distribution(80*16, 4)
# 绘图部分
from GGanalysis.gacha_plot import QuantileFunction
import matplotlib.cm as cm
def ht_yizhi(x):
return '全至少'+str(x-1)+'星'
# 计算分布
dist_list = [gg.FiniteDist([0])]
for i in range(1, 5):
dist_list.append(generate_gg_dist_list(calc_cdf(C, need_rank=i, allow_exchange=True, use_weapon_50=0)))
dist_list.dist = dist_list.dist[:i*80*4]
fig = QuantileFunction(
dist_list,
title='幻塔限定SSR意志抽取概率(含晶片兑换)',
item_name='意志',
text_head='本图不考虑50铸金兑换\n不保证模型准确,本图结果仅供参考\n全至少1星期望为153.35抽\n最差的情况下需要320抽\n全至少3星期望为510.91抽\n最差的情况下需要1280抽',
text_tail='@一棵平衡树',
max_pull=80*16,
mark_func=ht_yizhi,
mark_exp=False,
mark_max_pull=False,
is_finite=True)
fig.show_figure(dpi=300, savefig=True)
# fig.show_figure(dpi=72, savefig=False)
for i in range(1, 5):
dist_list.p_normalization()
print(i, dist_list.exp)
dist_list = [gg.FiniteDist([0])]
for i in range(1, 5):
dist_list.append(generate_gg_dist_list(calc_cdf(C, need_rank=i, allow_exchange=True, use_weapon_50=1)))
dist_list.dist = dist_list.dist[:i*80*4-50]
fig = QuantileFunction(
dist_list,
title='幻塔限定SSR意志抽取概率(含晶片及铸金兑换)',
item_name='意志',
text_head='本图考虑武器池有50铸金情况\n不保证模型准确,本图结果仅供参考\n全至少1星期望为112.00抽\n最差的情况下需要240抽\n全至少3星期望为476.52抽\n最差的情况下需要1200抽',
text_tail='@一棵平衡树',
max_pull=1250,
mark_func=ht_yizhi,
mark_exp=False,
mark_max_pull=False,
line_colors=cm.Purples(np.linspace(0.4, 0.99, 4+1)),
is_finite=True)
fig.show_figure(dpi=300, savefig=True)
# fig.show_figure(dpi=72, savefig=False)
for i in range(1, 5):
dist_list.p_normalization()
print(i, dist_list.exp)
C = DP_state_distribution(3000, 4)
dist_list = [gg.FiniteDist([0])]
for i in range(1, 5):
dist_list.append(generate_gg_dist_list(calc_cdf(C, need_rank=i, allow_exchange=False, use_weapon_50=0)))
fig = QuantileFunction(
dist_list,
title='幻塔限定SSR意志抽取概率(不进行兑换)',
item_name='意志',
text_head='不保证模型准确,本图结果仅供参考\n不进行兑换的情况下无法保证在有限抽内达成目标\n相信没有玩家会做这样的事',
text_tail='@一棵平衡树',
max_pull=3000,
mark_func=ht_yizhi,
mark_exp=False,
mark_max_pull=False,
line_colors=(cm.Greys(np.linspace(0.5, 0.9, 4+1))+cm.Reds(np.linspace(0.5, 0.9, 4+1)))/2,
is_finite=False)
fig.show_figure(dpi=300, savefig=True)
# fig.show_figure(dpi=72, savefig=False)
[/code]
需要注意的是,采用的策略是先抽完手上的抽数,然后再兑换。如果先兑换再继续抽,并不是最优的。
如果不使用武器池铸金,可以得到这样的分位函数:
TapTap
如果使用武器池铸金,需要的抽数会少一些,可以得到这样的分位函数:
TapTap
如果什么都不用,就硬抽就很夸张了,不过我相信没人会这样:
TapTap
嗯,基本上算完了。不过由于意志有和武器池铸金联系在一起,追求极致的话还需要把这两个联合在一起,但我觉得算了,懒得算。
5
1
2