你能用java.util.Random找到以前生成的随机数吗?

时间:2015-09-01 05:51:10

标签: java random

是否可以使用java.util.Random找到以前生成的随机数?

我在this网站上找到了相关信息,表示它使用的格式为LCG

number i + 1 =(a * number i + c)mod 2 48

其中number的第一个值是seed,并且每次调用nextInt(或类似的东西)时只是递增一个

有没有人有关于它是如何生成的更多信息?或者偏移值是什么?

编辑:我从openjdk

中找到了this源代码

3 个答案:

答案 0 :(得分:9)

,您可以获取之前的opacity : 1值而不存储它们(并且不知道@Andreas建议的Random对象的原始种子)。这是概念验证代码。它仅支持还原Random来电,但也可以还原其他来电。

nextLong()

用法示例:

public class InvRand {
    private static final long addend = 0xBL;
    private static final long multiplier = 0x5DEECE66DL;
    private static final long invMultiplier = 0xDFE05BCB1365L;
    private static final long mask = 0xFFFFFFFFFFFFL;

    private long seed;

    public InvRand(Random r) {
        seed = prevSeed(replicateSeed(r.nextLong()));
    }

    public long prevLong() {
        seed = prevSeed(seed);
        long b1 = seed >>> 16;
        seed = prevSeed(seed);
        long b2 = seed >>> 16;
        return (b2 << 32) + b1;
    }

    static long replicateSeed(long nextLong) {
        int nextM = (int)(nextLong & 0xFFFFFFFF);
        int nextN = (int)((nextLong - nextM) >> 32);

        long upperMOf48Mask = 0xFFFFFFFF0000L;

        long oldSeedUpperN = ((long)nextN << 16) & mask;
        long newSeedUpperM = ((long)nextM << 16) & mask;

        for (long oldSeed = oldSeedUpperN; oldSeed <= (oldSeedUpperN | 0xFFFF); 
                  oldSeed++) {
            long newSeed = (oldSeed * multiplier + addend) & mask;
            if ((newSeed & upperMOf48Mask) == newSeedUpperM) {
                return newSeed;
            }
        }
        throw new InternalError();
    }

    static long prevSeed(long seed) {
        return ((seed - addend) * invMultiplier) & mask;
    }
}

典型输出:

public static void main(String[] args) {
    Random rand = new Random();
    for(int i=0; i<20; i++) {
        System.out.println("next: " + Long.toHexString(rand.nextLong()));
    }
    InvRand ir = new InvRand(rand);
    for(int i=0; i<20; i++) {
        System.out.println("prev: "+Long.toHexString(ir.prevLong()));
    }
}

这里有两个问题。首先是按next: 76c8febd3eab0fd8 next: 19ea99b87b9c118e next: 2d69d148285ac86e next: f466d00f770361e7 next: ca069823ec343ea2 next: f570a154be288a23 next: 3f2f3844ad48b1ea next: d79ed82cd2e927e next: 97ffcecf7d9a5b0a next: d4e1a218dea3fc6f next: c54e390e8f9486fe next: 670052e4c52b230 next: ed13a7adac2ffc1c next: f4e41dc7ada0ea7d next: 56ffb122ab160d7a next: cd7ecd6d1236049b next: ce0597694257008c next: d32d6ef8c142a09b next: 2e8bb4f2356ea912 next: f5bf0eb275fb77df prev: f5bf0eb275fb77df // note numbers are going backwards now prev: 2e8bb4f2356ea912 prev: d32d6ef9c142a09b prev: ce0597694257008c prev: cd7ecd6d1236049b prev: 56ffb123ab160d7a prev: f4e41dc8ada0ea7d prev: ed13a7aeac2ffc1c prev: 670052e4c52b230 prev: c54e390f8f9486fe prev: d4e1a219dea3fc6f prev: 97ffcecf7d9a5b0a prev: d79ed83cd2e927e prev: 3f2f3845ad48b1ea prev: f570a155be288a23 prev: ca069824ec343ea2 prev: f466d00f770361e7 prev: 2d69d148285ac86e prev: 19ea99b87b9c118e prev: 76c8febd3eab0fd8 值复制随机种子。这部分(nextLong)基于GitHub的this code。下一个问题是反转种子计算公式,因此我们可以将种子从当前更改为前一个。目前OpenJDK的下一个公式是:

replicateSeed

所以它只是在模2 ^ 48的整数环中加法和乘法。这两种操作都是不可逆转的。对于添加,您可以在同一个环中减去。对于乘法,你必须计算反转乘数,这可以解决丢番图方程nextseed = (oldseed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL; 。解决它你得到x*0x5DEECE66DL+y*0x1000000000000L = 1(选择适合戒指的解决方案)。因此,要反转乘法,您只需要乘以x = 0xDFE05BCB1365L

答案 1 :(得分:2)

根据问题的确切含义,答案是

可以重新生成(&#34;找到&#34;)相同的随机数序列, if 你提供种子值,或者在构造函数上(new Random(seed))或使用setSeed(seed)

由于随机数是伪生成的,因此在指定种子之后生成的数字将始终相同。

证明

long seed = 54321;

Random r = new Random(seed);
for (int i = 0; i < 10; i++)
    System.out.print(r.nextInt(1000) + " ");
System.out.println();

r.setSeed(seed);
for (int i = 0; i < 10; i++)
    System.out.print(r.nextInt(1000) + " ");
System.out.println();

输出

928 451 642 402 522 773 977 704 893 115 
928 451 642 402 522 773 977 704 893 115 

答案 2 :(得分:1)

  

有没有人有关于它是如何生成的更多信息?

来自Random类的Java文档:

  

此类的实例用于生成伪随机流   数字。该类使用48位种子,使用a修改   线性同余公式。 (见唐纳德克努特,计算机艺术   编程,第2卷,第3.2.1节。)

现在下一部分是:

  

是否可以使用找到以前生成的随机数   java.util.Random中?

Random类实现的算法使用受保护的实用程序方法,该方法在每次调用时最多可提供32个伪随机生成的位。所以答案可能是,但如果你愿意,可以将它们保存在列表中。