通过构造函数错误地播种 Mersenne Twister

Incorrectly seeding Mersenne Twister via constructor

我的构造函数有什么问题?每次我调用一个应该生成随机数的函数(大约每五秒一次)时,它都会生成相同的数字。每个调用实例化下面这些对象之一。我以为我是用 m_rdoperator() 调用的输出随机播种 m_gen

我可以将m_rd()的结果传递给构造函数吗?签名是什么? Shuffler(std::random device& rd)?但这对用户来说会更加困难。

编辑:

实际上,如果可能的话,我更喜欢不需要向构造函数传递任何内容的解决方案。

shuffler.h

#include <random>

class Shuffler
{
private:
    std::random_device m_rd;
    std::mt19937 m_gen;
public:

    //! The default constructor. 
    Shuffler();

};

shuffler.cpp

#include "shuffler.h"

Shuffler::Shuffler() : m_gen(m_rd())
{
}

因为这个 example, you have to seed it and random_device 似乎没有成功*:

// do this once somewhere
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();


class Shuffler
{
private:
    std::mt19937 m_gen;
public:
    Shuffler()  : m_gen(seed) {}

};

*如前所述hererandom_device不是种子序列!

std::random_device 通常适用于此类事情,但可能并非在每个平台上都适用。虽然大多数平台的标准库根据一些底层 OS 随机功能(即 Linux 上的 /dev/urandom 或 Windows 上的 CryptGenRandom 来实现它,但它不是C++ 标准要求这样做。在某些平台上,高质量的随机生成器可能根本不可用,并且该标准允许 std::random_device 成为简单的静态种子 PRNG。如果是,每个 std::random_device 对象将生成相同的数字序列。

出于这些原因,您可能想回到简单的时间播种。标准 提供 std::chrono::high_resolution_clock:

class Shuffler
{
private:
    std::mt19937 m_gen;
public:
    Shuffler()
        : m_gen{static_cast<std::uint32_t>(
              std::chrono::high_resolution_clock::now().time_since_epoch().count()
          )}
    {}
};

std::chrono::high_resolution_clock通常具有纳秒或数百纳秒的分辨率。这足够高,以至于通过调用 high_resolution_clock 播种的两个 PRNG 不太可能最终使用相同的种子。但这也不能保证。例如,std::chrono::high_resolution_clock 在 macOS 上只有微秒分辨率,这可能不足以满足您的目的。

归根结底,这两种方法都不是完美的。您可能希望使用 std::seed_seq:

将两者结合起来
std::seed_seq make_seeds() {
    thread_local std::random_device rd;
    return {{
        static_cast<std::uint32_t>(std::chrono::high_resolution_clock::now().time_since_epoch().count()),
        rd()
    }};
}

// Cast away rvalue-ness because the standard random generators need
// an lvalue reference to their seed_seq for some strange reason
template <typename T>
T& identity(T&& t) { return t; }

class Shuffler
{
private:
    std::mt19937 m_gen;
public:
    Shuffler()
        : m_gen{identity(make_seeds())}
    {}
};

如您所见,这远非简单,而且仍不完美。有关播种和随机数生成器的更多信息,请参阅 these blog posts,然后您会想到您想要的。