为啥每次使用 std::random_device 和 mingw gcc4.8.1 运行都会得到相同的序列?

Posted

技术标签:

【中文标题】为啥每次使用 std::random_device 和 mingw gcc4.8.1 运行都会得到相同的序列?【英文标题】:Why do I get the same sequence for every run with std::random_device with mingw gcc4.8.1?为什么每次使用 std::random_device 和 mingw gcc4.8.1 运行都会得到相同的序列? 【发布时间】:2013-09-18 19:27:11 【问题描述】:

我使用以下代码来测试 C++ <random> 库。

为什么每次运行已编译的可执行文件都得到完全相同的序列? rd() 在编译时是确定性的吗?如何为每次运行获得不同的输出?

Windows 7 64 位上的 GCC 4.8.1。使用来自 http://nuwen.net/mingw.html 的 MinGW 分发。

编辑:我用 Visual Studio 测试了相同的代码。没有问题。输出是不确定的。这可能是我使用的 mingw gcc 4.8.1 中的一个错误。

#include <iostream>
#include <random>
using namespace std;

int main()
 random_device rd;
 mt19937 mt(rd());
 uniform_int_distribution<int> dist(0,99);
 for (int i = 0; i< 16; ++i)
    cout<<dist(mt)<<" ";
 
 cout <<endl;

【问题讨论】:

请提供平台和编译器。这绝对应该不会发生,即使是entropy() == 0。如果是这样,那就是一个错误。 @MM。不,random_device 不是这样工作的。 能否让编译器打印宏_GLIBCXX_USE_RANDOM_TR1 的内容?如果为 0,则使用带有固定种子的 mt19937 作为后备。 使用 gcc 4.9.2 的 mingw-w64 中仍然存在错误 有没有人尝试向 GCC 报告错误以便修复?还是要求太多? 【参考方案1】:

来自http://en.cppreference.com/w/cpp/numeric/random/random_device:

请注意,如果实现无法使用非确定性源(例如硬件设备),则 std::random_device 可以根据伪随机数引擎实现。

我希望一个体面的实现至少可以为 RNG 播种。

编辑:我怀疑他们故意选择每次都提供相同的序列,以表明流并不像承诺的那样随机。

【讨论】:

我同意。 stdlibc++ 的后备实现使用一个常量种子,我觉得它并不那么聪明(也没有解释)。 真正的失败在于一开始就拥有这种伪随机回退。 @ypnos:标准必须做一些事情来涵盖确定性平台上 C++ 实现的情况。但是在真实平台上这样做是一个巨大的实施质量问题。另请参阅How to succinctly, portably, and thoroughly seed the mt19937 PRNG?。【参考方案2】:

我得到了STL from MSFT的确认答复:

与 VC 不同,GCC 没有在 Windows 上实现 random_device 非确定性。 Boost 有,所以你可以使用 Boost.Random。

【讨论】:

我还没有找到合适的文档来说明 gcc 中需要哪个 boost lib -l 和什么 order boosts -l 以避免链接错误,并且还需要任何系统 -l。只是添加 -lboost-random-mgw48-mt-d-1_57 导致链接器抱怨没有 boost::random::random_device() 也没有 boost::random::~random_device()【参考方案3】:

您可能需要向构造函数传递一个参数:

https://gcc.gnu.org/onlinedocs/gcc-4.9.1/libstdc++/api/a00899.html

【讨论】:

【参考方案4】:

这是GCC bug,已在 GCC 9.2 中修复。

如果您遇到此问题,请更新您的编译器。 (例如,您可以从 MSYS2 获得新的 GCC。)

【讨论】:

@northerner 运行pacman -Syuu。有一个变化是它将关闭终端(和所有 msys2 程序)以继续;旧版本会要求您手动进行。如果发生任何这种情况,您必须重新启动 MSYS2 并再次运行相同的命令才能完成更新。 更新失败 ***.com/questions/63201980/… 另外我已经有 GCC 9.1 和 random_device 仍然无法工作。 @northerner 好吧,正如我所说,它已在 GCC 9.2 中修复,而您的版本比这更旧。尝试重新安装 MSYS2。【参考方案5】:

    GCC 没有正确实现 rd.entropy() - 它总是返回 0(至少在 Mac OS X 上)。

    不幸的是,似乎没有办法将额外的熵混合到 random_device 中,这很重要,因为它通常/经常(查看 Linux /dev/random 和 /dev/urandom,以及英特尔 RDRAND 实现)实现引擎盖下的伪随机数生成器。我希望能够通过注入我认为随机的东西与它的熵源产生的任何东西混合来提高它的输出。同样,由于该设备(或内核模块)内部实现了一种加密算法来处理它获得的熵位以生成其输出,我希望能够通过注入更多地“随机化”该过程我自己的数据与设备选择的任何熵混合。 例如,考虑 Java SecureRandom()。它不允许您设置种子(这确实会将其转换为 PRNG),但它会很乐意将您提供的内容与它用来“随机化”其输出的任何内容混合在一起。 p>

    我个人更喜欢 RDRAND。具有紧凑 C 接口的小型汇编库。以下是参考资料:

    David Johnson from Intel explains RDRAND on ***

    *** pointers to RDRAND library source for Windows, Linux, and Mac OS X

    Intel blog on RDRAND library, and a download link

【讨论】:

播种random_device 没有意义。如果它需要一个种子,那么它就是一个伪随机数生成器,而不是真正的随机数生成器,而 random_device 应该是这样的。 “种子”是一个不幸的词。您不会“播种”“真正的”随机设备。但由于 Linux 提供的随机设备(甚至是固件实现的 RDRAND)在它们的熵源和用户可用的输出之间涉及软件算法,混合来自其他源的随机性/熵不会损害结果,有时可能改进它。如果你是诚实的,我认为你应该撤回你的反对票。 我认为答案写得很混乱,如果你重写它,那么我可能会收回我的反对票。 伪随机生成随机抽取是有区别的。从理论上讲,它是这样工作的。给定一个弱随机源,例如可能是 1000 位,其中只有 100 位熵,首先您想使用提取器来获取 50 位,其中包含 50 位熵。在实践中,这使用加密散列函数。然后可以将结果用作生成器的种子,生成器将 50 位扩展到更多位以供您的应用程序使用。 (编造的数字。) 不错。我不知道你可以/dev/random。现在我知道了:add a file as entropy source 和 why writing to /dev/random...。将额外的熵异或到结果中是一种很好且有效的方法 - 缺点是显式。 我认为 Java 模型是最好的,因为它允许使用 SecureRandom 的程序的其余部分不知道所有这些细节和改进,而只是使用标准类的标准接口作为-是。 Java SecureRandom API 的另一个优点是它可以防止用户输入的“坏”随机性造成的损坏。 SecureRandom 输出只能通过额外的输入来提高(或保持相同的质量)。

以上是关于为啥每次使用 std::random_device 和 mingw gcc4.8.1 运行都会得到相同的序列?的主要内容,如果未能解决你的问题,请参考以下文章

在c ++ 11中使用一个随机引擎进行多分布

随机造数据的技巧总结

为啥 ListView onScroll() 被多次调用,每次都使用相同的参数量?

为啥每次递归都使用这么多的堆栈空间?

为啥我的标题每次渲染都已经使用 React.memo?

为啥 Apache Beam BigQueryIO 每次运行都使用相同的 JobId?