使用"<random>"的不可重现的随机数

Dav*_*vid 8 c++ random

我正在尝试创建一个为多个发行版生成随机数的类,同时保持它们的可重现性(通过设置初始种子).

代码似乎工作,直到我开始使用正态分布和奇怪的错误表面.这些主要是:

  • 如果我取消注释double a = rnd.rnorm(0.0, 1.0);-line(第40行)(即如果我rnorm在设置种子之前调用),则正态分布的第一个随机数不再匹配,随后的随机数再次匹配
  • 如果我从正态分布中检索奇数个随机数,则将正常随机数移一(例如,通过将第39行设置为int n = 3;)
  • 如果我一起做这两件事,随机数在另一个方向上移一个(领先)

现在我的问题是,是什么导致了这种奇怪的行为?我RNG是以错误的方式实施的吗?最重要的是,我该如何解决?

如果您想自己测试结果,可以使用此http://cpp.sh/9phre

或这个

#include <stdio.h>
#include <random>

// Class to create random numbers 
// Main functions to set the seed: setseed()
// create uniformly distributed values: runif()
// and normally distributed values: rnorm()
class RNG {
public:
    RNG(int seed = (int) time(0)) {
        setseed(seed);
    };
    ~RNG() {};
    void setseed(int newSeed) {
        re.seed(newSeed);
    };

    double runif(double minNum, double maxNum) {
        return dud(re, distUnifDbl::param_type{minNum, maxNum});
    };
    double rnorm(double mu, double sd) {
        return dnd(re, distNormDbl::param_type{mu, sd});
    };

private:
    // take the Mersenne-Twister Engine
    std::mt19937 re {};
    // create the uniform distribution
    using distUnifDbl = std::uniform_real_distribution<double>;
    distUnifDbl dud {};
    // create the normal distribution
    using distNormDbl = std::normal_distribution<double>;
    distNormDbl dnd {};

};

int main(int argc, char const *argv[]) {
    RNG rnd;
    int n = 4; // setting n to an odd number, makes _all_ normal numbers non-reproducible
    //double a = rnd.rnorm(0.0, 1.0); // uncommenting this, makes the _first_ normal number non-reproducible

    printf("Testing some Uniform Numbers\n");
    rnd.setseed(123);
    for (int i = 0; i < n; ++i) {
        printf("% 13.10f ", rnd.runif(0.0, 1.0));
    }
    rnd.setseed(123);
    printf("\n");
    for (int i = 0; i < n; ++i) {
        printf("% 13.10f ", rnd.runif(0.0, 1.0));
    }
    printf("\n");

    printf("\nTesting some Normal Numbers\n");
    rnd.setseed(123);
    for (int i = 0; i < n; ++i) {
        printf("% 13.10f ", rnd.rnorm(0.0, 1.0));
    }
    rnd.setseed(123);
    printf("\n");
    for (int i = 0; i < n; ++i) {
        printf("% 13.10f ", rnd.rnorm(0.0, 1.0));
    }
    printf("\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果

基本情况

设置n = 4和离开a评论时,我收到以下内容(这正是我想要/需要的;可重现的"随机"数字):

Testing some Uniform Numbers
 0.7129553216  0.4284709250  0.6908848514  0.7191503089 
 0.7129553216  0.4284709250  0.6908848514  0.7191503089 

Testing some Normal Numbers
-0.5696096995  1.6958337120  1.1108714913  0.9675940713 
-0.5696096995  1.6958337120  1.1108714913  0.9675940713 
Run Code Online (Sandbox Code Playgroud)

错误1

现在为了错误.设置n = 5(或任何奇数),我收到:

Testing some Uniform Numbers
 0.7129553216  0.4284709250  0.6908848514  0.7191503089  0.4911189328 
 0.7129553216  0.4284709250  0.6908848514  0.7191503089  0.4911189328 

Testing some Normal Numbers
-0.5696096995  1.6958337120  1.1108714913  0.9675940713  1.5213608069 
-0.0482498863 -0.5696096995  1.6958337120  1.1108714913  0.9675940713 
Run Code Online (Sandbox Code Playgroud)

这显然所有正常数字移动 1.统一数字保持不变(这是好的,我猜).

错误2

取消注释一行(即,设置种子之前调用rnd.rnorm(0.0, 1.0)一次),导致以下输出(带有或任何其他偶数)n = 4

Testing some Uniform Numbers
 0.7129553216  0.4284709250  0.6908848514  0.7191503089 
 0.7129553216  0.4284709250  0.6908848514  0.7191503089 

Testing some Normal Numbers
 0.9761557076 -0.5696096995  1.6958337120  1.1108714913 
 0.9675940713 -0.5696096995  1.6958337120  1.1108714913 
Run Code Online (Sandbox Code Playgroud)

这显然打破了第一个正常的随机数,再次保留了统一的数字.

错误3

将这两个点一起使用(将该行取消注释并将n设置为奇数),我得到了这个

Testing some Uniform Numbers
 0.7129553216  0.4284709250  0.6908848514  0.7191503089  0.4911189328 
 0.7129553216  0.4284709250  0.6908848514  0.7191503089  0.4911189328 

Testing some Normal Numbers
-0.4553400276 -0.5696096995  1.6958337120  1.1108714913  0.9675940713 
-0.5696096995  1.6958337120  1.1108714913  0.9675940713  1.5213608069 
Run Code Online (Sandbox Code Playgroud)

现在,第二个正常随机数被移到另一个方向(引导).

系统规格

我在Ubuntu 16.04上使用它 g++ --version g++(Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

更新

它似乎没有连接到特定的生成器,即替换std::mt19937 re {};with std:: linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647> re {};,或std::subtract_with_carry_engine<std::uint_fast64_t, 48, 5, 12> re{};结果具有相同的行为(但显然具有不同的数字).

n. *_* m. 6

void setseed(int newSeed) {
        re.seed(newSeed);
        dud.reset(); // <---- 
        dnd.reset(); 
    };
Run Code Online (Sandbox Code Playgroud)

分布具有内部状态.您需要重置它才能再次获得相同的序列.

  • 特别是,“std::normal_distribution”通常是有状态的,因为用于生成正态分布数字的几种常见算法每次运行都会生成两个这样的数字。 (3认同)