You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

逆向推导Java中Math.random()生成随机字符串的初始随机数

如何从Java Math.random()生成的字符序列反推初始种子

要精确推导Math.random()的初始种子,我们得先搞清楚Java中Math.random()的底层实现逻辑——它本质上是调用了一个静态Random实例的nextDouble()方法,而Random类基于**线性同余生成器(LCG)**生成伪随机数,这意味着我们可以通过已知的输出反向计算初始种子。

关键前提梳理

  1. 你的字典dict长度是39(26个小写字母+10个数字+3个符号),所以每个字符对应的索引i满足:Math.random()的结果落在区间[i/39, (i+1)/39)时,会生成该字符。
  2. Random类的核心LCG公式:
    • 种子更新:nextSeed = (currentSeed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
    • nextDouble()的计算:将生成的48位种子取高53位(通过两次next()调用拼接),再除以2^53,得到[0.0, 1.0)的数值。

精确推导步骤

1. 明确已知条件的约束范围

假设你已知前k个字符对应的索引i₁, i₂, ..., iₖ,先从第一个索引开始,计算Math.random()的区间:

int dictLength = 39;
// 第一个字符对应的索引,比如"j"对应索引9(a=0,j是第10个)
int firstIndex = ...; 
double low = (double) firstIndex / dictLength;
double high = (double) (firstIndex + 1) / dictLength;

2. 反向推导可能的种子范围

nextDouble()的结果x满足:x = (seed₁ >>> 11) / (1L << 53),其中seed₁是初始种子seed₀经过一次LCG更新后的种子。我们可以把区间转换为seed₁的可能取值:

long pow53 = 1L << 53;
long xLow = (long) Math.floor(low * pow53);
long xHigh = (long) Math.floor(high * pow53 - 1e-12); // 避免浮点精度误差

seed₁的形式为(value << 11) | r,其中value在[xLow, xHigh]之间,r是0到2047(2^11 -1)的整数。

3. 逆推初始种子并验证

LCG是可逆的,我们可以用seed₁反推出初始种子seed₀:

final long MULTIPLIER = 0x5DEECE66DL;
final long ADDEND = 0xBL;
final long MASK = (1L << 48) - 1;
// MULTIPLIER在模MASK下的逆元,预先计算好的
final long INV_MULTIPLIER = 0xDFE05BCB1365L;

// 遍历可能的value和r
for (long value = xLow; value <= xHigh; value++) {
    for (int r = 0; r < (1 << 11); r++) {
        long seed1 = (value << 11) | r;
        // 逆推初始种子seed0
        long seed0 = (seed1 - ADDEND) * INV_MULTIPLIER & MASK;
        // 创建Random实例验证
        Random random = new Random(seed0);
        // 验证第一个字符是否匹配
        double firstRandom = random.nextDouble();
        int calculatedIndex = (int) Math.floor(firstRandom * dictLength);
        if (calculatedIndex != firstIndex) {
            continue;
        }
        // 验证后续的字符序列是否和你能复现的一致
        boolean match = true;
        for (int expectedIndex : subsequentIndices) { // subsequentIndices是你已知的后续字符索引
            double nextRandom = random.nextDouble();
            int idx = (int) Math.floor(nextRandom * dictLength);
            if (idx != expectedIndex) {
                match = false;
                break;
            }
        }
        if (match) {
            System.out.println("找到初始种子: " + seed0);
            return;
        }
    }
}

优化提示

  • 如果只靠第一个字符,可能会有大量候选种子(因为区间范围很大),但只要你有后续2-3个字符的索引,就能快速过滤到唯一的正确种子。
  • 浮点计算时要注意精度问题,比如用1e-12调整xHigh,避免因为浮点舍入丢失正确的取值范围。

内容的提问来源于stack exchange,提问作者user11238130

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 11:27:49