debugging 如何在Java中记录和播放随机数?

jc3wubiy  于 2023-08-06  发布在  Java
关注(0)|答案(3)|浏览(111)

我正在开发一个应用程序,其中包括生成一组数据的生成阶段。该生成涉及多个循环,其中一些循环生成随机数以便做出如何继续的决定。这意味着生成的数据集每次都不相同。目前,所有的随机数都是基于单个Random对象生成的,每个对象都是用no-argx 1 m1n1x构造函数构造的。
我试图跟踪一些错误,这些错误只在特定条件下使用生成的数据集发生。给定一个有错误的数据集,我知道为什么应用程序中会出现错误,但我试图解决的问题是生成阶段首先是如何生成有错误的数据集的。然而,这并不容易调试,因为99%的时间生成阶段不会生成有错误的数据集。
因此,我希望有一种方法来记录和回放正在生成的数字,以重现错误发生的特定条件。我在网上搜索过这样的框架,但没有找到任何东西。不幸的是,“record randoms”返回的大多是关于随机记录的结果,而“repeatable randoms”返回的大多是关于不可重复的RNG的结果!
我知道生成阶段的代码只调用java.util.Random.nextInt(int)方法,这在一定程度上简化了问题。我开始考虑自己创建一个可重复的随机框架。

public class RepeatableRandom extends Random {
    private static final Object LOCK = new Object();
    private static final Random RANDOM = new Random();

    private static enum Mode { PASSTHROUGH, RECORD, PLAYBACK; }
    private static Mode mode = Mode.PASSTHROUGH;
    // ...synchronized setters for each of the three modes...

    private static Map<Integer,Integer> invocationCount = new HashMap<>();
    private static Map<Integer,List<Integer>> randoms = new HashMap<>();

    @Override
    public int nextInt(int n) {
        switch (mode) {
            case Mode.PASSTHROUGH:
                return RANDOM.nextInt(n);
            case Mode.RECORD:
                if (!randoms.containsKey(n)) {
                    randoms.put(n,new ArrayList<>());
                }
                int nextInt = RANDOM.nextInt(n);
                randoms.get(n).put(nextInt);
                return nextInt;
            case Mode.PLAYBACK:
                synchronized(LOCK) {
                    int i = invocationCount.get(n);
                    int nextInt = randoms.get(n).get(invocationCount);
                    invocationCount.put(n,i+1);
                    return nextInt;
                }
        }
    }

    public void loadRandomsFromFile(String filename) { //... }
    public void saveRandomsToFile(String filename) { //... }
    public void clear() { // ... }
}

字符串
在生成阶段将所有对Random的引用替换为RepeatableRandom之后,我将编写如下测试代码:

RepeatableRandom.setRecordMode();
do {
    RepeatableRandom.clear();
    dataSet = generateDataSet();
while (!isBuggyDataSet(dataSet));
RepeatableRandom.saveRandomsToFile(FILENAME);


然后,在我的代码中,我可以这样写:

RepeatableRandom.setPlaybackMode();
RepeatableRandom.clear();
RepeatableRandom.loadRandomsFromFile(FILENAME);
executeMainApplication();


然后,我可以在调试模式下运行,并加载断点,最后找出为什么会生成这个特定的错误数据集。
但我确信我不是第一个遇到这个问题的人。我不太愿意仅仅为这个用例创建一个全新的定制框架。我觉得一定有什么东西已经在那里,但我就是找不到它!解决这类问题的正确方法是什么?

mftmpeh8

mftmpeh81#

如果你有多个随机对象,那么在你的测试代码中,你可以设置一个已知种子的主随机对象。使用该主示例为其他子随机示例提供种子。通过重复主种子,所有其他子示例将获得它们在上次使用主种子时获得的任何种子,前提是它们以相同的顺序开始。
如果你不想重复,那么就从时钟或其他地方播种大师。

ewm0tg9j

ewm0tg9j2#

哇代码还真多。。
你意识到你总是可以从同一个地方开始一个随机序列,使用一个种子?当你创建一个新的随机数时,你可以传入一个种子值,如果你使用相同的种子两次,那么你每次生成的数字都是相同的。
选择一个种子,运行你的测试,如果它们没有显示出你想要的行为,那么增加种子,再试一次。当你得到你想要的东西时,记下种子,从那时起一直使用这个数字。

ar7v8xwq

ar7v8xwq3#

开始时捕获随机种子,并在测试期间将其重置为相同的种子。您可能希望更改代码以依赖全局Random提供程序,然后可以将此类策略全局附加到应用程序。

相关问题