游戏中的洗符石问题
问题描述
《幻唐志》中的宠物符石系统是一个数学逻辑很复杂的概率系统。
宠物符石共三组,每组两个,共六个符石。每次洗练可以重置其中一组,并从本次洗出的候选符石中自选 2 个替换原来的两个符石。
符石的属性由“颜色”和“字”组成:
- 颜色:五种五行,共 5 种;
- 字:八卦字 8 种,外加“无字”1 种,共 9 种。
因此候选石的总组合数为:
本文要求最终三组符石都成为“同色同字”,且不允许无字。也就是说,有效目标组合只有:
种。
每次洗练时,出现的候选石数量的原始概率为:50%几率出现2个候选石,35%几率出现3个候选石,10%几率出现4个候选石,5%几率出现5个候选石。
但存在保底机制:
- 若连续 9 次没有出至少 4 个候选石,则第 10 次必出至少 4 个;
- 若连续 19 次没有出 5 个候选石,则第 20 次必出 5 个。
我们的目标是计算完成三组同色同字所需洗练次数的数学期望。
出石数量 的稳态分布
状态定义
定义状态:
其中:
- :连续未出至少 4 个候选石的次数;
- :连续未出 5 个候选石的次数。
显然:
状态总数为:
每洗一次,状态发生转移。转移规则为:
若本次洗出 个候选石:
- 若 ,则 重置为 0;否则 加 1;
- 若 ,则 重置为 0;否则 加 1。
同时,当 且 时,本次出石数量强制为:
当 时,本次强制为:
否则按原始概率分布。
平稳分布求解
由于状态空间有限且不可约,该马尔可夫链存在唯一平稳分布 ,满足:
其中 是 的状态转移矩阵。
该方程可以通过数值方法精确求解。解出平稳分布后,可得到出石数量 的边际分布:
数值结果如下:
| 2 | 0.4776 |
| 3 | 0.3343 |
| 4 | 0.1090 |
| 5 | 0.0791 |
可见,保底机制使 5 石概率从原始 5% 提升到约 7.9%,2 石概率从 50% 下降到约 47.8%。
单次洗练成功概率
3.1 固定目标组合的成功概率
当第一组已经锁定某个目标组合 后,后续两组都必须洗出至少两个 。
单个候选石恰好是 的概率为:
若某次洗出 个候选石,令 表示其中组合 的个数,则:
成功条件为:
因此:
利用出石数量的稳态分布 ,得到锁定目标后单次洗练成功概率:
代入数值:
即约 0.143%。
第一组任意有效组合的成功概率
第一组尚未锁定具体组合,只要本次洗出的候选石中存在某一种非无字组合出现至少两次,即可成功锁定目标。
有效组合共有 40 种,无字组合有 5 种。
设某次洗出 个候选石,令 表示其中属于有效组合的个数。
由于每个候选石属于有效组合的概率为:
所以:
给定 时,这 个有效组合均匀分布在 40 种具体组合上。
若这 个有效组合全部互不相同,则没有任意一个有效组合出现至少两次,洗练失败。
因此,给定 时,成功的条件概率为:
其中:
是降阶乘。
对于 或 ,显然成功概率为 0。
所以:
则第一组单次洗练成功概率为:
代入数值:
即约 5.57%。
总期望次数
完成三组需要:
- 第一组洗出任意同色同字非无字组合,锁定目标;
- 第二组洗出与第一组相同的组合;
- 第三组洗出与第一组相同的组合。
每次洗练相互独立,且每次只影响当前组。
因此第一组所需次数服从几何分布,期望为:
锁定目标后,第二组和第三组每次成功概率均为 ,期望分别为:
所以总期望次数为:
代入数值:
得到:
因此期望值约为:
这与模拟程序运行结果 1400~1450 次 基本一致。
这里给出一下模拟程序的示例代码:
package main
import (
"fmt"
"math/rand/v2"
"time"
)
func main() {
r := rand.New(rand.NewPCG(uint64(time.Now().UnixMilli()), 1))
var lessThen4, lessThen5 int
randOne := func() int {
var count int
switch {
case lessThen5 == 19:
count = 5
case lessThen4 == 9:
count = 4
if r.IntN(3) == 0 {
count++
}
default:
n := r.IntN(20)
switch {
case n < 10:
count = 2
case n < 17:
count = 3
case n < 19:
count = 4
default:
count = 5
}
}
switch count {
case 5:
lessThen5 = 0
lessThen4 = 0
case 4:
lessThen5++
lessThen4 = 0
default:
lessThen5++
lessThen4++
}
result := make([]int, count)
for i := range count {
v := r.IntN(45)
if v < 5 {
continue
}
for j := range i {
if result[j] == v {
return v
}
}
result[i] = v
}
return -1
}
result := 0
for range 100000 {
var cur int
for range 3 {
for {
result++
v := randOne()
if cur == 0 {
if v > 0 {
cur = v
break
}
} else if v == cur {
break
}
}
}
}
fmt.Println(float64(result) / 100000)
}结论
在考虑保底机制后,完成三组同色同字非无字宠物符石所需的洗练次数期望约为:
即大约 1420 次。
实际游戏中考虑到波动,体感通常会在 1400~1600 次 之间。
