是的,您可以在Random不存储它们的情况下获得之前的值(并且不知道Random像@Andreas建议的对象的原始种子).这是概念验证代码.它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;
}
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
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()));
}
}
Run Code Online (Sandbox Code Playgroud)
典型输出:
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
Run Code Online (Sandbox Code Playgroud)
这里有两个问题.首先是按nextLong值复制随机种子.这部分(replicateSeed)基于GitHub的代码.下一个问题是反转种子计算公式,因此我们可以将种子从当前更改为前一个.目前OpenJDK的下一个公式是:
nextseed = (oldseed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL;
Run Code Online (Sandbox Code Playgroud)
因此,它只是在模2 ^ 48的整数环中的加法和乘法.这两种操作都是不可逆转的.对于添加,您可以在同一个环中减去.对于乘法,你必须计算反转乘数,这可以解决丢番图方程x*0x5DEECE66DL+y*0x1000000000000L = 1.解决它你得到x = 0xDFE05BCB1365L(选择适合戒指的解决方案).因此,要乘以乘法,您只需要乘以0xDFE05BCB1365L.
| 归档时间: |
|
| 查看次数: |
606 次 |
| 最近记录: |