《用Python实现原神中算法》文字版
第一次在B站上发文章,可能排版什么的,不是很好看,也希望大家多多包涵。
由于 上周发的视频 内容较难,看到很多同学在 评论区 和 弹幕 里面说想要这个视频的文字版,所以想了想,还是给大家写出来了,主要是代码的内容以及详细的注释,当然也希望大家自己去实现一下代码,不要光听我说,听见了没?听见了。
早上四点起来写的,比较仓促,有什么写错的地方,也请在评论区指出,或者有什么问题,也可以在评论区提问,我看到后都会进行回复。本文主要包含四个内容:
抽卡算法、状态机图、香菱火圈、拓扑排序

抽卡算法

当然,我实现的只是一个最简单的版本,原神怎么实现的,我不可能知道,只是给出一个大概的算法框架,给对这方面有疑惑的朋友一点灵感,你也可以自己去设计这方面的算法。
1、抽一次

第 3 行,定义一个 chou 函数,cishu代表目前是上一次五星限定角色抽完以后的第几次抽卡,wai代表这期间歪了几次,取值为 [0, 1]。
第 4 - 5 行,如果次数小于等于 73 次,概率为 0.6%,这里统一乘上了 100。
第 6 - 7 行,如果次数等于第 90 次,概率为 100%,说明必定出五星。
第 9 行,是一个一次函数,代表从 73 抽以后,每抽一次,出五星的概率增加 6%。
第 10 - 11 行,如果随机出来的概率,不在边界概率 p,那么代表不是五星,可能是一些辣鸡的武器,返回 2。
第 12 - 13 行,否则,一定出五星,这时候如果歪过一次,则这次一定不歪,返回 0。
第 14 行,如果没有歪过,那么 50% 是歪,50% 不歪,返回 0 和 1 中随机的一个数即可。
2、十连三金需要的抽卡次数

第 21 行,shilian 代表已经十连的次数,初始化为 0。
第 22 行,cishu 代表第几抽。
第 23 行,wai 代表歪过几次。
第 26 行,x 代表本次十连中有多少个五星角色。
第 27 - 28 行,连续抽 10 次。
第 29 - 32 行,代表抽到了五星限定角色,则次数和歪的次数都要重置。
第 33 - 36 行,代表歪了,则歪的次数加一,次数重置。
第 38 行,代表抽到奇奇怪怪的东西,次数+1。
第 39 行,如果有 3 个五星角色,则输出是第几次 十连 的时候达到的这个盛况。

状态机图

这里主要涉及到的知识点是图的概念,并且讲到的是一个状态机,利用邻接矩阵来存储图,并且通过随机函数进行状态转移,从而实现随机序列的生成。
1、C语言实现

第 4 - 9 行,定义的是一个C语言中的二维数组,其中第 i 行 第 j 列的数值,代表了第 i 号节点和第 j 号节点的有向关系,当且仅当 adj[i][j] 为 1 时,第 i 号节点 能够走到 第 j 号节点。
第 11 - 13 行,定义了一个函数,用于输出第 aniId 号节点的值。
第 17 行,pos 代表当前处于哪个节点。
第 18 行,定义一个死循环,让程序可以无限进行输出。
第 19 号,随机生成下一个节点的编号 x。
第 20 - 22 行,如果 adj[pos][x] 为 0,代表 pos 不能直接走到 x,那么 x 需要重新随机 ,所以这里采用了 while 循环的方式;如果 adj[pos][x] 为 1,代表 pos 可以直接走到 x 这个节点,则跳出循环。
第 23 行,输出 x 这个节点(播放 x 号动画)。
第 24 行,将 x 赋值给 pos,代表一次迭代,下次就是从 x 开始往后随机。
2、Python实现

第 2 行,引入 random,用于做随机数生成。
第 4 - 9 行,定义一个 Python 2维列表,含义和 C语言 类似。只不过C语言用的是花括号,Python用的是方括号。
第 11 - 12 行,定义了一个函数,用于输出第 aniId 号节点的值。
第 16 行,类似 C语言中的 main 函数。
第 17 行,pos 代表当前处于哪个节点。
第 18 行,定义一个死循环,让程序可以无限进行输出。
第 19 号,随机生成下一个节点的编号 x。
第 20 - 21 行,如果 adj[pos][x] 为 0,代表 pos 不能直接走到 x,那么 x 需要重新随机 ,所以这里采用了 while 循环的方式;如果 adj[pos][x] 为 1,代表 pos 可以直接走到 x 这个节点,则跳出循环。
第 23 行,输出 x 这个节点(播放 x 号动画)。
第 24 行,将 x 赋值给 pos,代表一次迭代,下次就是从 x 开始往后随机。
大体上,和C语言的语句结构,还是非常相似的。

香菱火圈


第 6 行,获取标准输出的句柄。
第 7 行,记录一个时间 t。
第 8 行,定义一个循环。
第 9 行,定义火圈的位置。
第 10 行,定义香菱的位置。
第 11 行,定义火圈的半径为 R。
第 12 行,利用余弦函数,计算香菱火圈的 x 坐标,由于光标的 y 轴是 x 轴的两倍,所以这里火圈的 x 轴半径是 2R。
第 13 行,利用正弦函数,计算香菱火圈的 y 坐标。
第 14 行,清空屏幕,避免火圈残留。
第 15 - 16 行,设置光标当前位置,并且打印 "香菱"。
第 17 - 18 行,设置火圈当前位置,并且打印 "火圈"。
第 19 - 20 行,FALSE 代表将光标进行隐藏。

拓扑排序

1、建图的过程

首先,定义邻接矩阵,代表有向图的点和边的关系。
visited 数组代表当前这个节点是否被访问过。
degree数组代表当前这个节点的入度(有多少条边指向它)。
遍历任意两个点,如果 adj[i][j] 为 1,则给 j 的入度 加 1。
2、拓扑排序的过程

整个过程就是迭代去找 尚未被访问 且 入度为 0 的点,存储到 u 中:

如果找不到则跳出循环,否则标记 已访问:

然后将它转换成字母后进行输出:

最后将 u 对应所有出边的 端点 的入度减一,此所谓删边的操作:

重复迭代,这个拓扑排序的过程就完成了。

好啦,如果你想学习更多算法,可以 关注公众号:夜深人静写算法,回复 666 ,获取 算法学习路线。有什么相关问题,也可以从公众号找到我的联系方式进行咨询,或者从这个链接直达(无法放站外链接,所以需要自己复制粘贴到浏览器打开):https://ufbva3m5zn.feishu.cn/docs/doccnIQ95UPr5yabjQg2bFHl79f#
