保存并加载随机数生成器状态

问题描述 投票:0回答:1

考虑到 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 random
1个回答
0
投票

我尝试了以下选项:

  1. 按照链接答案中所述通过反射检索种子对我来说不起作用(产生
    java.lang.reflect.InaccessibleObjectException
  2. 使用相同的种子创建一个新的 prng 并将其发展到相同的状态
  3. 序列化、(保存和加载)、反序列化同一对象

对我来说,第一个选项似乎是最有问题的,取决于实现细节。第二个选项并不优雅,因为它浪费了计算时间。第三个选项似乎最接近我的要求。

接下来,我为遇到相同要求的任何人附上我的测试源代码:

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());
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.