考虑到 Java 随机数生成器,我想“保存”并稍后“加载”其状态,这意味着它应该继续生成可重复的相同随机数,尽管它是随机数生成器的不同实例:
public static void main(String[] args) {
final java.util.Random prng1 = new java.util.Random(4);
int lastInt = 0;
// Generate arbitrary amount of random numbers
for (int i = 1; i < 21; i++) {
lastInt = prng1.nextInt();
}
// TODO: Save state
// TODO: Load state later, continuing with the same random numbers as if it were the same random number generator
final java.util.Random prngRestored = new java.util.Random(lastInt);
System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestored.nextInt());
}
是否可以实现这一目标?如果可以,如何实现?
一个想法是也保存生成的数字量,并让“恢复”的 prng 与初始 prng 具有相同的种子,迭代相同数量的数字,以达到相同的内部状态,但这似乎浪费计算电源。
我尝试了以下选项:
java.lang.reflect.InaccessibleObjectException
)对我来说,第一个选项似乎是最有问题的,取决于实现细节。第二个选项并不优雅,因为它浪费了计算时间。第三个选项似乎最接近我的要求。
接下来,我为遇到相同要求的任何人附上我的测试源代码:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
public class PrngSaveLoadTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
long initialSeed = 4;
final Random prng1 = new Random(initialSeed);
int amountOfSteps = 0;
// Generate arbitrary amount of random numbers
for (int i = 1; i <= 21; i++) {
prng1.nextInt();
amountOfSteps++;
}
// TODO: Save state
// TODO: Load state later, continuing with the same random numbers as if it were the same random number generator
// Option 1: Use reflection to get internal seed of prng1 - does not work, throws exception
//final Random prngRestoredBySeed = new Random(getSeed(prng1));
//System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestoredBySeed.nextInt());
// Option 2: Progress the second prng instance the same amount of numbers - works
final Random prngRestoredByProgress = new Random(initialSeed);
progressPrng(prngRestoredByProgress, amountOfSteps);
System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestoredByProgress.nextInt());
// Option 3: Serialize, save, load, deserialize the prng instance
byte[] serializedPrng = serializePrng(prng1);
Random prngRestoredBySerialization = deserializePrng(serializedPrng);
System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestoredBySerialization.nextInt());
}
/**
* See https://stackoverflow.com/a/29278559/1877010
*/
private static long getSeed(Random prng) throws NoSuchFieldException, IllegalAccessException {
long theSeed;
try {
Field field = Random.class.getDeclaredField("seed");
field.setAccessible(true);
AtomicLong scrambledSeed = (AtomicLong) field.get(prng); //this needs to be XOR'd with 0x5DEECE66DL
theSeed = scrambledSeed.get();
return (theSeed ^ 0x5DEECE66DL);
} catch (Exception e) {
//handle exception
throw e;
}
}
private static void progressPrng(Random prng, long amountOfSteps) {
for (long i = 1; i <= amountOfSteps; i++) {
prng.nextInt();
}
}
private static byte[] serializePrng(Random prng) throws IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(prng);
return baos.toByteArray();
}
}
private static Random deserializePrng(byte[] serializedPrng) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedPrng);
ObjectInputStream in = new ObjectInputStream(bais)) {
// Method for deserialization of object
return ((Random) in.readObject());
}
}
}