slime/show/RANDOM_PROPERTIES.md

14 KiB
Raw Blame History

随机数性质:怎样从坐标图案反推种子候选

前面的 01–04 在做同一件事:给定一批种子和坐标,把它们检查得更快。这一章换一个角度:如果我们已经知道哪些坐标应该是史莱姆区块,能否利用随机数公式,先排除大量种子,再求出值得检查的候选?

我们以一个固定坐标的实心 5×5 图案为例。目标是寻找一个世界种子,让这 25 个格子同时满足要求。我们不知道每格产生的随机数具体是多少,只知道它们应该通过史莱姆判断。因此,这里的“反推”是根据多个条件缩小候选范围,结果可能有多个种子。

当前方法分成三步:筛低 18 位的可能取值,补上中间 17 位,再求出高 13 位的共同解。 最后执行完整的 Java Random 验证。

下面解释筛选和求解的过程,以及每一步的数学依据。首次输出筛选还没有完整覆盖拒绝重抽的例外;这个限制在文末说明。

1. 先区分种子、内部状态和随机输出

对坐标 $(x_i,z_i)$,原始史莱姆公式先计算坐标项:

T(x_i,z_i)=F(x_i)+G(z_i).

坐标项包含平方、乘法和加法,必须遵循原始 Java 的整数回绕与符号扩展规则,完整说明见 OUTER_SUM.md。下面用 d_i=T(x_i,z_i) 表示第 i 格的坐标项。

设世界种子的低 48 位为 $w$,合并史莱姆公式与 Java Random 初始化中的两个 XOR,得到:

A=\mathtt{0x5DEECE66D},\qquad C=A\mathbin{\mathrm{xor}}987234911=\mathtt{0x5E434E432}.

每个格子的首次随机输出按下面三步产生:

u_i=((w+d_i)\mathbin{\mathrm{xor}}C)\bmod2^{48},

v_i=(Au_i+11)\bmod2^{48},

q_i=\left\lfloor v_i/2^{17}\right\rfloor.

这三个数的含义不同:

符号 含义
w 待求的世界种子低 48 位
d_i 第 i 格的坐标项,坐标确定后就能计算
u_i Random 初始化后的 48 位内部状态
v_i 内部状态推进一次后的结果
q_i 取出 v_i 的高 31 位得到的首次输出

其中,mod 表示取余数。例如 $8194\bmod8192=2$。模 2^k 相当于只保留一个数的低 k 位。

对被接受的首次输出,nextInt(10) 返回 $q_i\bmod10$,返回 0 就表示史莱姆区块。我们先利用“q_i 能被 10 整除”的条件生成候选,随后完整验证。

2. 为什么只考虑世界种子的低 48 位

Random 初始化时只保留低 48 位。因此,在同一个坐标上,如果两个原始世界种子 W_1,W_2 相差整数倍的 $2^{48}$,初始化状态就相同,以后的随机数序列也相同:

W_2=W_1+k2^{48} \quad\Longrightarrow\quad \mathrm{slime}(W_2,x,z)=\mathrm{slime}(W_1,x,z).

这条性质覆盖完整的随机数序列,包括拒绝重抽。搜索时可以先求低 48 位,原始 64 位输入的高 16 位对应同一组史莱姆判断结果。

这里讨论的是史莱姆判断的等价关系,不能据此断言整个 Minecraft 世界相同。

3. 把待求的 48 位分成三部分

按通常从高位到低位的书写顺序,这三部分是:

高 13 位 h | 中间 17 位 m | 低 18 位 p

对应数值关系:

w=p+2^{18}m+2^{35}h.

各部分的取值范围是:

0\le p<262144,\qquad 0\le m<131072,\qquad 0\le h<8192.

补上中间 17 位后,得到低 35 位:

\ell=p+2^{18}m,\qquad w=\ell+2^{35}h.

这三个部分的处理方式不同:低 18 位先筛选,中间 17 位目前主要枚举,高 13 位通过分段同余条件求解。

4. 低 18 位为什么能提前筛选

首次输出是:

q_i=v_i\gg17.

右移 17 位,就是去掉内部状态的低 17 位。于是,q_i 的最低位,对应 v_i 的第 17 位,位编号从 0 开始。

一个数要能被 10 整除,首先必须是偶数。我们只要知道 v_i 的低 18 位,就能知道它的第 17 位,从而判断首次输出的奇偶。

为什么计算这 18 位时,可以暂时不管世界种子的高位?因为加法、乘法和逐位 XOR 的低 k 位,只依赖输入的低 k 位。更高位不会反过来改变低位。

因此,令 $p=w\bmod2^{18}$、$d_{i,18}=d_i\bmod2^{18}$,只需要计算:

a_i=(p+d_{i,18})\bmod2^{18},

u_{i,18}=a_i\mathbin{\mathrm{xor}}(C\bmod2^{18}),

v_{i,18}=(Au_{i,18}+11)\bmod2^{18}.

最后检查:

v_{i,18}<2^{17}=131072.

小于这个分界值,说明第 17 位为 0,首次输出是偶数;否则就是奇数。

坐标项也参与计算。 这条性质说的是“种子的低 18 位,加上该格坐标项的低 18 位,足以决定首次输出奇偶”。换坐标后,结果可能改变。

5. 怎样从一个图案生成低 18 位候选表

只有一个“5×5”的形状还不够,需要知道锚点在哪。已有样例的锚点是:

(x_0,z_0)=(51069,419124).

图案包含的 25 个坐标为:

(x_i,z_i)=(x_0+\Delta x,z_0+\Delta z), \qquad\Delta x,\Delta z\in{0,1,2,3,4}.

生成表的过程可以按下面的顺序理解:

  1. 计算 25 个坐标项,分别保留低 18 位。
  2. 尝试一种种子的低 18 位取值 $p$。
  3. 用这个 p 检查 25 格的首次输出奇偶。
  4. 有一格为奇数,就淘汰这个 $p$;全部为偶数,就保存。
  5. 对 p=0,1,\ldots,262143 重复这个过程。

这相当于为每格求出一个允许集合,再对 25 个集合求交。

看两个实际取值,就能理解“保留”和“淘汰”是什么意思:

种子的低 18 位 p 检查的格子 v_{i,18} 结果
0 (51069,419124) 53282 偶数条件通过
0 (51069,419125) 178468 失败,整个 p=0 淘汰
3948 (51069,419124) 1118 偶数条件通过
3948 (51069,419125) 73664 偶数条件通过

p=3948 在其余格子上也通过,所以进入最后的候选表。

固定锚点 $(51069,419124)$,把要求通过的图案从 1 格逐步扩大到 5×5。每次保留已有格子,再加入扩大后新增的一圈格子,剩余取值数量如下。表中的 0 表示尚未加入任何格子条件,1 表示只检查锚点这一格:

要求通过的图案 剩余低 18 位取值数
0 262144
1 131072
2×2 20383
3×3 3385
4×4 427
5×5 84

单格恰好保留一半取值,是因为加坐标项、XOR 和奇数乘法在模 2^{18} 下都是一一对应的变换。继续加入格子,只会增加条件,所以候选数量只能保持不变或减少。

这 84 种取值就是“该坐标图案允许的世界种子低 18 位候选表”。

对固定图案,这一步可以作为预处理,只做一次。后续直接遍历这 84 种取值,给它们补上更高位,不必让所有完整种子先查一次表。

这里的 84 只适用于样例中的具体坐标。移动锚点或改变形状后,需要重新筛选;如果两个图案对应格子的坐标项低 18 位全部相同,就可以复用这张低位候选表,但完整随机输出仍可能不同。

6. 中间 17 位目前怎样处理

取出一个通过筛选的 $p$,再枚举:

m=0,1,\ldots,131071.

每次拼出一种低 35 位取值:

\ell=p+2^{18}m.

然后把这个 \ell 交给高 13 位求解器:是否存在某个 $h$,能让所有格子满足首次输出的模 10 条件?如果没有,就淘汰这个 $\ell$。

因此,当前没有另一张能直接消除中间 17 位枚举的通用表。样例需要处理:

84\times2^{17}=11010048

种低 35 位取值。低 18 位筛选避免了大量无效工作,但中间位的枚举成本仍然存在。

7. 为什么固定低 35 位后,高位可以单独求解

现在固定一种 $\ell$,世界种子写成:

w=\ell+2^{35}h.

选择 35 的原因是 XOR 常量 C 恰好需要 35 位:

2^{34}\le C<2^{35}.

它只会翻转低 35 位,不会碰到未知的高 13 位。

这里还要保留坐标加法的进位。对第 i 格,把 h=0 时的输入拆成:

(\ell+d_i)\bmod2^{48}=L_i+2^{35}H_i.

H_i 包含坐标加法产生的进位,也包含负坐标项在模 2^{48} 下的表示。加入 h 后,低位 L_i 不变,高位成为 $(H_i+h)\bmod8192$。

XOR 只改动 $L_i$,因此初始化状态满足:

u_i(h)=(u_i(0)+2^{35}h)\bmod2^{48}.

再推进一次状态:

v_i(h)=(v_i(0)+Ah,2^{35})\bmod2^{48}.

最后取首次输出,去掉状态低 17 位。新增量本来就是 2^{35} 的整数倍,不会改变被去掉的低位余数,所以:

q_i(h)=(q_i(0)+Ah,2^{18})\bmod2^{31}.

这里的 18 来自 $35-17$。

这是利用不同位域分离得到的平移关系。一般的 XOR 并不具有这种加法性质。若切在 34 位,C 仍会翻转未知部分的一位,上面的直接平移就不成立;切在 36 位等更高位置也可以分离,但剩下可解析的高位就更少了。

8. r_i,b_i 是怎样实际算出来的

它们来自一个确定的计算结果。选定 \ell 和第 i 格的坐标,先暂时令 $h=0$,按照原始公式计算:

u_i(0)=((\ell+d_i)\mathbin{\mathrm{xor}}C)\bmod2^{48},

v_i(0)=(Au_i(0)+11)\bmod2^{48},\qquad q_i(0)=\left\lfloor v_i(0)/2^{17}\right\rfloor.

再把 q_i(0) 除以 $262144=2^{18}$:

r_i=q_i(0)\bmod262144,\qquad b_i=\left\lfloor q_i(0)/262144\right\rfloor.

r_i 是余数,也就是输出的低 18 位;b_i 是商,也就是输出的高 13 位。例如,如果某格算出 $q_i(0)=524290$:

524290=2+262144\times2,

那么 $r_i=2,b_i=2$。这只是拆分数字的例子,不是前面样例某格的实测输出。

改变 h 后,该格的首次输出就是:

\boxed{q_i(h)=r_i+2^{18}\bigl((b_i+Ah)\bmod8192\bigr).}

b_i 是随机输出的一部分,h 是待求种子的一部分,不能把两者当成同一个数。 r_i,b_i 在当前 \ell 和坐标下固定,不同格子通常具有不同的 $r_i,b_i$,但所有格子共用同一个未知 $h$。

9. 为什么先求 $t$,再换回 h

为了让规则更容易处理,令:

t=Ah\bmod8192.

用 e_i 表示换元后该格输出的高 13 位:

e_i=(b_i+t)\bmod8192,\qquad q_i=r_i+2^{18}e_i.

A 是奇数,和 8192=2^{13} 互质,所以这个换元只是在重新排列 8192 个编号,不会重复或遗漏。

具体来说:

A\bmod8192=1645,\qquad 1645\times4965=997\times8192+1.

因此,求到 t 后,可以直接换回:

\boxed{h=4965t\bmod8192.}

这里用到了“奇数乘数在模 2^k 下有逆元”的性质。如果完整状态 v 已知,同样可以用模逆恢复上一状态:

u=A^{-1}(v-11)\bmod2^{48}.

但首次输出已经丢掉状态低 17 位,而史莱姆观察只提供一个布尔结果。因此,当前搜索仍需组合多个格子的条件来求候选。

10. 每格到底对 t 提出什么要求

我们要让首次输出能被 10 整除。偶数条件已经由低 18 位筛选保证,现在还需要能被 5 整除。

因为:

2^{18}\equiv4\equiv-1\pmod5,

所以:

q_i=r_i+2^{18}e_i\equiv r_i-e_i\pmod5.

于是,每格提出的要求是:e_i 除以 5 的余数,必须与 r_i 相同。

完整写法为:

q_i\equiv0\pmod{10} \iff r_i\equiv0\pmod2\ \text{且}\ e_i\equiv r_i\pmod5.

如果高位求解器单独接收一个没有预先筛过的低 35 位取值,发现某格 r_i 为奇数,就会立即返回无候选。

11. 回绕点是什么,为什么它会改变规则

考虑:

e_i=(b_i+t)\bmod8192.

可以把它想成一个从 0 数到 8191 的计数器。到 8192 时,就回到 0。

例如 $b_i=8190$:

t b_i+t 取模后的 e_i
0 8190 8190
1 8191 8191
2 8192 0
3 8193 1
4 8194 2

t=2 就是回绕点。一般来说,回绕点为:

\tau_i=8192-b_i.

点之前,$e_i=b_i+t$;从这个点开始,$e_i=b_i+t-8192$。由于 $0\le b_i,t<8192$,相加最多回绕一次。

把上一节的要求代入,就得到:

\begin{cases} t\equiv r_i-b_i\pmod5,&0\le t<\tau_i,\ t\equiv r_i-b_i+2\pmod5,&\tau_i\le t<8192. \end{cases}

第二种要求多了 2,是因为 $8192\bmod5=2$。若 $b_i=0$,回绕点在 8192,超出 t 的有效整数范围,所以这个格子在整个范围里都不回绕。

12. 用两个格子的数值例子,把求解串起来

下面的 r_i,b_i 是用于讲解的示意数值,不是 5×5 样例的实测参数。

第一格取 $r_1=2,b_1=8190$。它要求 $e_1\bmod5=2$,回绕点在 $t=2$:

  • t<2 时,要求 $t\bmod5=2$。
  • t\ge2 时,要求 $t\bmod5=4$。

第二格取 $r_2=0,b_2=1$。它要求 $e_2\bmod5=0$,回绕点在 $t=8191$:

  • t<8191 时,要求 $t\bmod5=4$。
  • t\ge8191 时,要求 $t\bmod5=1$。

把两格条件合并:

t 的区间 第一格要求的余数 第二格要求的余数 共同解
[0,2) 2 4 无
[2,8191) 4 4 区间中除以 5 余 4 的数
[8191,8192) 4 1 无

因此,允许的 t 是:

t=4,9,14,\ldots,8189.

取 $t=4$,换回种子的高位:

h=4965\times4\bmod8192=3476.

检查两个格子的首次输出:

e_1=(8190+4)\bmod8192=2,\qquad q_1=2+2^{18}\times2=524290,

e_2=(1+4)\bmod8192=5,\qquad q_2=2^{18}\times5=1310720.

两个输出都能被 10 整除,所以 h=3476 是这两格共同允许的高位取值。真实图案对 25 格做同样的求交,最后拼成完整种子,再验证。

13. 最后说明首次输出模型的边界

上面的状态与输出变换恒等式是精确的。覆盖限制来自用“首次输出能被 10 整除”生成候选。

Java nextInt(10) 遇到:

q\ge2147483640=2^{31}-8

时,会拒绝这个输出,继续推进内部状态,再取一个输出。例如 2147483640 虽然能被 10 整除,也不会直接被接受。

因此会有两种情况:

  • 首次输出符合我们的候选条件,但被拒绝,重抽后失败。最终精确验证会剔除这种假阳性。
  • 首次输出不符合候选条件,例如是奇数,但处在拒绝尾部,重抽后成功。它可能在低位筛选阶段就被淘汰,最终验证无法补回这种漏解。

所以,当前流程准确验证所有输出的候选,但不能宣称已经找齐完整 Java Random 语义下的所有解。

低 18 位的 84 种取值、正文中的筛选数量和两个格子的示意例子,都可以按照上面的计算步骤独立核对。