用Python计算明日方舟2021龙门幸运墙期望
按照去年的惯例,方舟今年春节的时候也整了个红包盲盒。

比起去年简单粗暴的直接送,今年的盲盒实际上增加了两层隐性的保底机制:第一层是每天有两次机会而非一次,两次尝试取收益更高的结果;第二层是如果不幸成为了开出小红包的天选之人,第二天追加一次开包机会。
这种保底机制使得在今年的活动里玩家几乎不可能吃到最大的那层低保(8.025×10^31分之1),但是也使得用纯数学方式计算期望变得极其复杂。但是作为一个高数挂过一次的人我也不可能硬算啦……因此这次我使用暴力美学,用计算机跑他个几百万次,跑出来的结论就可以近似看作期望了。顺便为了测试一下 B 站新搞的代码块好不好用,我这次在文中把代码贴出来试试看。
1. 基础档位
首先可以看出玉有六个档位:2/3/4/5/6/800。可以用 2.5 种方式确定档位:设置好每个档位对应的玉的数量,对档位进行随机,随后将档位换算成对应玉的数量;直接对六个玉的数量进行随机抽取;或者对 2-8 进行随机,如果出现 7 则舍去该结果再次随机直到出现到几个数字中的一个为止,最后再将结果乘以 100。第一和二种方式本质上没什么区别,但第三种因为玉的数量没有 700 这个档位,这么做的话稍微有点点傻……
我选择的是第一种方式,用字典记录六个档位对应的奖励,然后对档位进行随机抽取。
2. 流程
每一天的流程很简单:开第一个包→开第二个包→根据前一天的开包情况决定有没有第三个包→取最大值→进入下一天。
但是实现“开第三个包”的方式有很多。最笨的实现当然可以根据鹰语的描述,用 400 与前一天的结果进行比较来决定第三个包是否存在,但是我选择了一种对人类比较友好的方式:立一个“需要抽第三次”的 flag,该 flag 在第一天的默认值是 False,在随后每天两次抽取结束后将会检查 flag 的状态,如果为真,则抽取第三次;如果为价,则认为第三次抽取到的玉数量为 0。执行完这一分枝后将再次判断今天的收获,如果小于 400,则将 flag 反转为真,反之则翻转为假,进入第二天。
代码如下:
3. 计算期望并输出
计算期望就是总共获得的玉除以总尝试次数这个没什么好说的,主要核心在怎么写流程……因此直接上代码。
4. 输出每次尝试获得的玉总数和对应频数
在脚本的一开始我定义了一个名为all_results
的空列表来存储每次尝试(跑完 14 天后的总数)的结果。在 Python 当中可以直接调用min
和max
函数对整个列表中的数据进行取最大值和最小值操作,也可以在导入 collections.Counter 模组之后很方便的统计频数。(注意:Counter()的结果不能直接拿来用)
因为我需要计算频数,所以我需要这个列表。如果没有这个需求的话,直接存储上一次尝试的结果,然后把这一次尝试的结果和上一次进行两两比较,然后舍弃掉不要的那个结果。这样可以占用更少的系统资源。(但是都用 python 了……)
因为我的 python3.9 好像装不上 matplotlib,我也懒得折腾,所以就把文件导出成了 csv 之后扔进了 SPSS。
5. 完整代码和结果
把其他框架部分补充完整之后,试着跑他个十遍的一百万次。

我也尝试了一下跑一亿次,好家伙给我跑了一个小时……

把解释器换成 pypy 之后,总运行时间得到了惊人的提升。

就当我直呼pypy yes的时候我发现哪里不对劲,仔细一看发现小数点后的结果全被舍掉了……
pypy:别扯什么准确了,你就说我算得快不快吧!
但是至少从这一亿多次结果可以看出来,想要吃到 2800 的天选保底,难度还真挺大……
像极了大学期末考老师拼命捞人的样子。
代码全文如下: