重新启动C ++中的随机数序列

时间:2018-07-18 06:17:08

标签: c++ random

简短的问题:如何在不干扰已经创建的随机序列的情况下生成随机数?或者,如何在编译时未知的位置重新启动随机数序列?

长的问题:我有一个程序使用随机数序列,并且在测试过程中始终使用相同的随机种子(在这种情况下为1)。但是,随着类的更改,它们可能会调用该随机序列,从而产生一个问题,即主序列中的随机数不再相同,这是因为该序列已被那些更改的类所推进。

这是一个简短的片段,显示了潜在的现象。让我们假设foo已被添加或更改过,从而改变了随机数序列。

#include<iostream>

void foo(void);

int main() {
    srand(1);
    std::cout << rand() << '\t' << rand() << std::endl;

    srand(1);
    foo();
    std::cout << rand() << '\t' << rand() << std::endl;

    return 0;
}

void foo(void) {
    // Some other function/class member that uses rand( )
    rand();
}

至少在我的机器上是哪个值?

18467  41
6334   18467

第一种方法(很显然是不切实际的实现)是简单地计算/计算rand()在主函数及其所有被调用函数/类中被调用的次数,然后重新进行种子和使用虚拟循环以将序列推进到正确位置。不幸的是,如上所述,这是不切实际的,因此需要一种不同的方法。

我试图找到代码的第一个可能的解决方案是创建一个特定于该类或函数的本地rand()序列,并从中提取数字,而保持原始序列不变。

我尝试查找代码的第二种可能的解决方案是从某个任意位置重新启动该序列,但是我既不知道如何获得该位置,也不知道如何在该位置重新启动它。

那么,在不特别知道rand()被调用了多少次的情况下,如何保存序列中的位置,然后在重新播种和/或可能已对rand()进行其他调用之后重新启动该序列?或者,或者在不干扰已经进行的rand()序列的情况下,在那些类/函数中生成随机数?

2 个答案:

答案 0 :(得分:3)

请勿使用srand() / rand()srand() / rand()实际上是单个全局生成器。 它没有提供读取状态的方法,因此使用它的类的交错调用只会相互干扰。

C ++具有更好的库<random>,并且许多生成器可以具有多个未耦合的实例。如果您希望不同的类使用不同的生成器,那就是您的答案。

您应该将srand() / rand()视为C遗留物。 C ++具有更加成熟和完善的随机数库。

std::default_random_engine通常是单元测试的不错选择,因为序列的统计特征通常是非关键的。 如果它不是密码或非常精确的统计应用程序,那么它也可能也适合您的程序代码。

如果您的申请是一个严格的统计申请typedef,则可以选择生成器,并且(在相关情况下使用auto)是必要的,因此您可以根据需要更改方法。生成器都是鸭式兼容的(现代的OO方式!),因此应该易于砍切和更改。

srand() / rand()都不允许您“检查”设置后的状态。

但是,如果需要,所有<random>生成器都允许您序列化和反序列化其内部状态。

因此,如果您希望某段代码重复上一个序列,则可以一次运行转储该状态,然后在解决问题时修改代码以强制使用该状态。

https://en.cppreference.com/w/cpp/numeric/random

如果您有一些主要代码和一些测试脚本,请务必在主要代码中使用与测试脚本不同的生成器实例。 考虑使用不同的生成器进行不同的测试。

如果您想在失败的测试之前插入测试以“探究”问题(通常的做法),请在插入的代码中至少使用一台生成器实例。

识别错误的最佳实践是尝试设计一种非随机测试,无论将来如何更改,该测试都会暴露该错误。 除非您在允许测试脚本“强制”生成器状态的主要代码中公开API,否则这可能很难。

有些人不喜欢仅存在于允许进行测试检测的API,而在加密应用程序中则只是“从不做”。

全局状态通常是错误的设计。忘了C ++,C应该总是提供(类似的东西):

void srand_state(unsigned seed, RAND_STATE* state);
int rand_state(RAND_STATE* state);

其中RAND_STATE是实现的定义。 然后指定RAND_STATE可以普通复制。 因此:

RAND_STATE saved;
memcpy(&saved,state,sizeof(RAND_STATE));

该实现可确保以后按任意顺序对rand(state)rand(&saved)进行的任何数量的调用在按每次被调用的时间顺序配对时都将返回相同的值。

C PRNG有点可忽略的不良关系。一些旧的实现更糟糕,没有用。

答案 1 :(得分:1)

您可以使用<random> header中的C ++随机数生成器来生成独立于rand()的随机数。每个生成器都有其独立于rand()和其他生成器的状态。您还需要一个分发对象来生成:

#include <iostream>
#include <random>

int main() {
    std::default_random_engine rng{1}; // or some other seed
    std::default_random_engine rng2{rng}; // you can create a copy of the state

    std::uniform_int_distribution<> dist{1, 100}; // range of numbers you want

    // use the first engine
    std::cout << dist(rng) << '\t' << dist(rng) << std::endl;

    // Since each engine has its own state, functions
    // you call will not affect the sequence.

    // use the second engine
    std::cout << dist(rng2) << '\t' << dist(rng2) << std::endl;

    return 0;
}

在我的机器上,这会产生以下结果:

1       14
1       14