随机 也有 历史
共享随机流,让一次操作的结果与草坪上其他事件相连。
约 2 分钟
随机生成器携带状态。取一个值会推进状态,重新播种则会替换状态。互不相关的系统共享生成器时,它们的历史会在下一次抽取处相遇。
随机 流 不止 一条
游戏自有的全局生成器、库打乱生成器与对象自有生成器,各有独立状态。进程还会从 random_ 初始化另一个引擎。把它们当成一条序列,会得到错误的依赖关系。
全局游戏生成器从种子 4357 开始,原始结果有 31 位。有界整数通过取模产生,浮点辅助函数则缩放一个值,再舍入为 float32。11共享游戏生成器游戏自有 MT19937 从 4357 开始,返回低 31 位,有界整数取模,浮点抽取除以 2147483647.0 后转换为 float32。分支清点找到 38 处全局播种调用,其中 30 处传入 time(NULL)。此外还存在对象自有生成器与使用 random_device 播种的进程引擎。这些游戏播种不影响 libc++ 打乱引擎。0x1034ff9440x103608ff40x1036090b40x10360921c
直到 4.2.4.399 版本,进化的结果选择调用 std:。其库引擎与摩登传送门队列等其他列表打乱共享。重置游戏的全局生成器,不会重置这个库引擎。22独立的 libc++ 打乱随机流导入的 __rs_get 与 __rs_default 符号标识双参数 std::random_shuffle。前向排列通过带拒绝采样的 uniform_int_distribution 取值。PlayCover 实时捕获记录了匹配种子 5489 的 mt19937 原始输出、完整排列与选择;宿主库检查属于独立证据。记录的 iPad 结果印证指定路线,但未识别 iPad 加载的库。进化与摩登传送门共享该库随机流。0x1009a92800x1001210440x10001bf384.2.6.401 版本改用神器专用的生成器选择进化结果,详见4.2.6.401 版本中的进化神器选择。
游戏的全局生成器以 1790000000 重新播种 · 31 位输出
- 01428991966
- 11930012957
- 21056160899
- 31924511133
- 4799111086
- 51919418955
- 62130897800
- 7352640671
库引擎mt19937 · 种子 5489 · 32 位输出
- 03499211612打乱
- 1581869302打乱
- 23890346734打乱
- 33586334585
- 4545404204
- 54161255391
- 63922919429
- 7949333985
古埃及传送门列表 ra, explorer, explorer, pharaoh → ra, pharaoh, explorer, explorer
分步查看
- 1两条序列。 只有当某处从生成器取值时,它才会前进。
- 2游戏抽取。 从游戏生成器取出三个值,使它前进到位置 3;库引擎没有移动。
- 3一次库打乱。 打乱古埃及的四槽传送门列表,消耗库引擎的 3 个输出;游戏生成器没有移动。
- 4按时钟重新播种。 以 time(NULL) = 1790000000 为游戏生成器播种,会替换它的状态:它从该种子的第一个输出重新开始。库引擎保持原来的位置。
多 抽 一次 , 下 一个 答案 就 移动 了
设想一个生成器的接下来几个值,分别交给手套概率、掉落概率与放置选择。若另一项操作在掉落之前消耗了一个值,掉落例程就会拿到序列中的另一部分。
两个功能之间不需要直接调用,共享状态已经足够。如果抽取发生了,即使结果没被使用,生成器也已经前进。
击杀奖励例程说明了这一点:即使植物没有对应奖励概率,它仍可能抽取。神器分支也可能先抽出数值,再覆盖它们。一个功能消耗的抽取次数,也是它影响后续消耗者的一部分。
打乱还增加了另一项变量。排列含 项的列表需要 次分布调用,而拒绝采样每次调用可以消耗不止一个引擎值。因此,改变候选数量也可以改变随机流前进的距离。
游戏的全局生成器种子 4357 · 31 位输出
- 02146374468手套概率
- 1699692587另一次抽取
- 21213834231掉落概率
- 31920714022放置选择
- 4994957275
- 52082945813
| 原计划 | 实际抽取 | |
|---|---|---|
| 手套概率 | 0 · 2146374468 | 0 · 2146374468 |
| 掉落概率 | 1 · 699692587 | 2 · 1213834231 |
| 放置选择 | 2 · 1213834231 | 3 · 1920714022 |
分步查看
- 1原计划。 手套概率、掉落概率与放置选择依次取走第 0、1、2 个输出。
- 2多抽一次。 另一项操作先取走第 1 个输出:掉落概率改为得到第 2 个输出,放置选择得到第 3 个,尽管两个功能互不调用。
多个 功能 重 置 同 一个 生成 器
共享游戏生成器有 38 处重新播种调用,其中 30 处传入 time(NULL)。这是横跨植物、掉落与神器的模式。时钟分辨率为一秒,每次重新播种都会替换生成器已经累积的状态。33跨系统共享的时钟重新播种分支清点找到共享播种封装的 38 处调用,其中 30 处传入 time(NULL)。已对应的时钟调用方包括加权掉落选择器、金币泉同类例程、扭蛋抽取、三份指令等价的僵王掉落实现,以及两条地锯草路径。掉落选择器在有界抽取前立即播种;扭蛋在抽取入口、后续选择逻辑之前播种。这些位置都替换 0x104e3e580 的状态。第二次播种会丢弃此前插入的抽取;只有该次播种之后的抽取,才会移动后续消耗者的位置。libc++ 打乱引擎是另一个对象。0x1034fca380x10198019c0x10197f7a40x1010952480x1000e172c0x101eeab000x10229a9c80x100b21ec80x100b24b1c
这些调用方都重置同一个对象。因此,掉落例程可以改变植物或神器之后读取的随机状态,正如植物可以改变后续掉落使用的状态。
两次相同范围的抽取,若各自紧接相同秒数的重新播种,就会得到相同首项。两次重置之间的抽取会被第二次重置丢弃。在第二次重置之后、下一个消耗者之前发生的抽取,才会改变该消耗者收到的输出。
地 锯 草 还 多 了一 处 顺序 错误
地锯草攻击例程先重新播种,再检查植物是否为等级 5。低于该等级时,它不进行可选抽取:改变了共享状态,却没有自己使用一个值。44攻击例程重置其他系统的随机状态地锯草攻击例程先调用 time(NULL) 并重新播种,再检查植物是否为等级 5。较低等级分支在重置后不进行抽取。其特效对象暴击路径也在 bounded-100 抽取之前立即播种。相同种子产生相同首项;播种后插入的其他抽取可以改变某个消耗者拿到的输出。0x100b21e980x100b21ec80x100b248980x100b24b1c
把等级检查提前,可以消除这一处不取值的重置,却不能修复更广泛的关联:其他使用时钟种子的调用方仍会替换共享状态。共同问题在于局部功能重置了进程级依赖。
以 1790000000 重新播种31 位输出
- 01428991966抽取 → 66
- 11930012957其他
- 21056160899其他
- 31924511133
- 4799111086
- 51919418955
再次以 1790000000 重新播种31 位输出
- 01428991966抽取 → 66
- 11930012957
- 21056160899
- 31924511133
- 4799111086
- 51919418955
以 1790000001 重新播种31 位输出
- 01811357021抽取 → 21
- 11236193707
- 2396618650
- 31172432243
- 41635916231
- 5158427370
分步查看
- 1重新播种,然后抽取。 某个调用方以 1790000000 重新播种并立即抽取:该状态的第 0 个输出在上界 100 下得到 66。
- 2中间的抽取。 其他消耗者取走同一状态的第 1、2 个输出。
- 3同一秒内再次重新播种。 另一个调用方以 1790000000 重新播种:状态被替换,第 1、2 个输出不再起作用,它以相同上界进行的第一次抽取重复得到 66。
- 4下一秒。 以 1790000001 重新播种会开始另一条序列;它的第一次抽取得到 21。
好的 生成 器 也 修 不好 错误 的 选择 循环
扭蛋神器的五项选择器构造累积权重,却在访问每项时重新抽取。这与只抽一次、再找到该值所在区间不同。
累积总量为 、总权重为 时,其理想化选择份额为
五项等权时,份额为 20%、32%、28.8%、15.36%、3.84%。输入权重相等,输出频率却不相等。55在累积扫描内部重新抽取五项选择器构造累积权重,但循环回边返回随机抽取位置。第 i 项的份额为此前各项 (1-Cj/T) 的乘积再乘 Ci/T。五项等权时,理想化份额为 0.2、0.32、0.288、0.1536、0.0384。等权示例展示算法,不把尚未对应的资源权重向量当作此处输入。浮点区间包含上端点,还允许一个未选中任何项的剩余情况。0x1010952240x1010953e40x10109540c
这种偏差远大于普通小范围取模时的微小偏差。累计权重选择应当只抽取一次,再寻找包含该值的区间;在每次比较时重新抽取会改变分布。
注释
每条论断的依据:代码路径,以及追踪到它的原生地址。
- 1
共享游戏生成器
游戏自有 MT19937 从 4357 开始,返回低 31 位,有界整数取模,浮点抽取除以 2147483647.0 后转换为 float32。分支清点找到 38 处全局播种调用,其中 30 处传入 time(NULL)。此外还存在对象自有生成器与使用 random_device 播种的进程引擎。这些游戏播种不影响 libc++ 打乱引擎。
0x1034ff9440x103608ff40x1036090b40x10360921c - 2
独立的 libc++ 打乱随机流
导入的 __rs_get 与 __rs_default 符号标识双参数 std::random_shuffle。前向排列通过带拒绝采样的 uniform_int_distribution 取值。PlayCover 实时捕获记录了匹配种子 5489 的 mt19937 原始输出、完整排列与选择;宿主库检查属于独立证据。记录的 iPad 结果印证指定路线,但未识别 iPad 加载的库。进化与摩登传送门共享该库随机流。
0x1009a92800x1001210440x10001bf38 - 3
跨系统共享的时钟重新播种
分支清点找到共享播种封装的 38 处调用,其中 30 处传入 time(NULL)。已对应的时钟调用方包括加权掉落选择器、金币泉同类例程、扭蛋抽取、三份指令等价的僵王掉落实现,以及两条地锯草路径。掉落选择器在有界抽取前立即播种;扭蛋在抽取入口、后续选择逻辑之前播种。这些位置都替换 0x104e3e580 的状态。第二次播种会丢弃此前插入的抽取;只有该次播种之后的抽取,才会移动后续消耗者的位置。libc++ 打乱引擎是另一个对象。
0x1034fca380x10198019c0x10197f7a40x1010952480x1000e172c0x101eeab000x10229a9c80x100b21ec80x100b24b1c - 4
攻击例程重置其他系统的随机状态
地锯草攻击例程先调用 time(NULL) 并重新播种,再检查植物是否为等级 5。较低等级分支在重置后不进行抽取。其特效对象暴击路径也在 bounded-100 抽取之前立即播种。相同种子产生相同首项;播种后插入的其他抽取可以改变某个消耗者拿到的输出。
0x100b21e980x100b21ec80x100b248980x100b24b1c - 5
在累积扫描内部重新抽取
五项选择器构造累积权重,但循环回边返回随机抽取位置。第 i 项的份额为此前各项 (1-Cj/T) 的乘积再乘 Ci/T。五项等权时,理想化份额为 0.2、0.32、0.288、0.1536、0.0384。等权示例展示算法,不把尚未对应的资源权重向量当作此处输入。浮点区间包含上端点,还允许一个未选中任何项的剩余情况。
0x1010952240x1010953e40x10109540c