如何用Java记录和回放randoms?

时间:2016-12-15 22:41:07

标签: java debugging random

我正在开发一个应用程序,其中包括生成一组数据的生成阶段。生成涉及许多循环,其中一些循环生成随机数,以便决定如何继续。这意味着每次生成的数据集都不相同。

我正在尝试跟踪一些仅在特定条件下使用生成数据集发生的错误。鉴于有缺陷的数据集,我知道为什么应用程序中会出现错误,但我正在尝试解决的问题是生成阶段如何首先生成错误的数据集。然而,这不容易调试,因为99%的生成阶段不会生成错误的数据集。

由于这个原因,我希望有一种方法来记录和回放正在生成的数字,以重现发生错误的特定条件。我在网上搜索过这样的框架,但我还没有提出任何建议。不幸的是,“记录randoms”主要是关于随机记录的结果,而“可重复的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();

然后我可以在调试模式下运行一系列断点,最后找出为什么生成这个特定的错误数据集。

但是我确信我不是第一个遇到这个问题的人。我觉得为这个用例创建一个全新的定制框架并不舒服。我觉得那里肯定有东西,但我找不到它!解决这类问题的正确方法是什么?

2 个答案:

答案 0 :(得分:0)

哇,这是很多代码......

你是否意识到你总是可以使用种子从同一个地方开始随机序列?创建新的Random时,您可以传入种子值,如果您使用相同的种子两次,那么您生成的数字每次都是相同的。

选择种子,运行测试,如果他们没有显示您之后的行为,则递增种子并再试一次。当你得到你想要的东西时,记下种子并从那时起始终使用那个数字。

答案 1 :(得分:-1)

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