是否可以使用java.util.Random
找到以前生成的随机数?
number i + 1 =(a * number i + c)mod 2 48
其中number的第一个值是seed,并且每次调用nextInt(或类似的东西)时只是递增一个
有没有人有关于它是如何生成的更多信息?或者偏移值是什么?
编辑:我从openjdk
中找到了this源代码答案 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个伪随机生成的位。所以答案可能是否,但如果你愿意,可以将它们保存在列表中。