【问题标题】:Why do these 4 different random number generator functions produce the same series of numbers?为什么这 4 个不同的随机数生成器函数会产生相同的数字序列?
【发布时间】:2018-02-11 13:55:22
【问题描述】:

我正在调试的一个神经进化程序在每次调用时都不会产生随机值。在程序中,网络对象的向量使用以下语句进行初始化:

vector<Network> population(POPULATION_SIZE, Network(sizes, inputCount));

为什么我认为程序不会收敛到最优解的原因是,前 100 名总是相同的。当以这种方式初始化网络时,连接权重和神经元偏差(每个)都使用以下类函数进行初始化:

double Network::randDouble(double low, double high) {
    /*default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
    uniform_real_distribution<double> distribution(low, high);
    return distribution(generator);*/

    /*srand(time(NULL));
    double temp;
    if (low > high) {
        temp = low;
        low = high;
        high = temp;
    }
    temp = (rand() / (static_cast<double>(RAND_MAX) + 1.0)) * (high - low) + low;
    return temp;*/

    /*mt19937 rgn(std::chrono::system_clock::now().time_since_epoch().count());
    uniform_real_distribution<double> gen(low, high);
    return gen(rgn);*/

    default_random_engine rd;
    uniform_real_distribution<double> gen(low, high);
    auto val = std::bind(gen, rd);
    return val();
}

3 个被注释掉的部分是先前尝试生成所需功能的方法。在每种情况下,它们为每个网络产生相同的数字(从 1 个权重到另一个,但不是 1 个网络到另一个)。尝试的方法基于此处的答案:

  1. c++-default_random_engine creates all the time same series of numbers
  2. http://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution

此外,无论有没有种子,第二种方法都会产生相同的结果。我肯定错过了什么。

另一个虽然可能无关紧要的问题是,使用 this 的函数可以使用 OpenMP 并行化,并且当并行调用时,结果可能相同。

【问题讨论】:

  • 用 '''static''' 为所有 3 个变量添加前缀。

标签: c++ c++11 random


【解决方案1】:

您的问题是每次生成数字时都在初始化(播种)随机生成器。在简单的srand() 情况下,您应该在程序启动期间只调用一次srand(),然后每次需要一个号码时调用rand()。在更复杂的情况下,您应该只构建一次生成器(在整个程序运行中),并根据需要多次使用它。

【讨论】:

  • srand(),如果在 main 中调用,是否对类中的函数有效?
  • @DerekSmith:是的。
  • 我发现了问题。创建网络向量时,它一定只创建了 100 个副本。只需为所需的 100 人提供新网络即可解决此问题。
【解决方案2】:

C++11 标准随机数引擎(以及大多数其他随机数生成器)实际上是 随机数序列的生成器。伪随机意味着序列是可重复的。每次给定的伪随机生成器播种相同的种子时,它总是会产生相同的序列。 (但这并不是你的代码中发生的事情。请继续阅读。)

在 C++11 中,播种发生在随机数引擎被实例化的时候。这意味着您需要为每个伪随机序列实例化引擎一次。您的代码在每次调用 Network::randDouble() 方法时为引擎播种的方式,您不能期望获得引擎设计生成的伪随机序列。相反,您将从通过调用 system_clock::... 或 time() 方法播种的序列中获得一系列第一个数字。

对 system_clock::now().time_since_epoch().count() 的调用返回整数个周期的时间。 period 指的是由 time_since_epoch() 返回的模板类 std::chrono::duration 的特化。默认情况下,时间段可能是秒,这可以解释为什么所有 Network 对象在每次调用 Network::randDouble() 时都获得相同的种子。

如果你想为每个网络使用不同的序列,你最好在 Network 类的 c-tor 中实例化伪随机引擎,并为它的每个对象使用不同的种子网络类。这意味着引擎或指向引擎对象的指针应该是该类的成员。

例子:

class Network {

...
protected:
    mt19937 rd;
...
}

Network::Network(int rndseed) :
    rd(rndseed)
{
...
}

double Network::randDouble(double low, double high) {

    uniform_real_distribution<double> gen(low, high);
    auto val = gen(rd);
    return val;
}

为了确保伪随机引擎的每个实例都获得不同的种子,您可以使用像结果整数这样简单的东西。如果你想使用系统时钟,即使你使用std::chrono::high_resolution_clock,要保证每次的种子都不一样要困难得多。 CPU 非常快,您需要特别注意确保您使用的时钟计数实际上在两次调用之间发生了变化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多