如何以安全便携的方式播种随机数生成器?

How to seed a random number generator in a safe and portable way?

提问人:Fareanor 提问时间:2/28/2020 最后编辑:NathanOliverFareanor 更新时间:2/28/2020 访问量:1317

问:

背景:

开始,建议使用 a 而不是 time 来播种随机数生成器。如果我们看一下相关文档,我们可以读到:std::random_device

std::random_device如果非确定性源(例如硬件设备)不可用于实现,则可以使用实现定义的伪随机数引擎来实现。在这种情况下,每个对象可以生成相同的编号规则std::random_device

强调我的

现在让我们考虑以下示例:

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution distr(0, 9);

    // Print a sequence of 10 uniformly distributed random integers
    for(std::size_t i = 0; i < 10; ++i)
        std::cout << distr(gen) << ' ';

    return 0;
}

如果系统/平台不提供非确定性源,则该程序可能/将始终为每次运行生成相同的数字序列(实际上在我的平台上就是这种情况)。

在这种情况下,远比用当前时间播种随机数生成器更糟糕:std::random_device

std::mt199937 gen(std::chrono::high_resolution_clock::now().time_since_epoch().count()); // time seed

问题:

我担心的是,如果有人想编写一个程序,该程序将依赖于一些随机生成的数字,并且是必需的:

  1. 便携
  2. 保证非确定性随机性

那么就不合适了。
另一方面,始终使用时间种子将是一个非常令人沮丧的“解决方案”,因为如果给定的平台有一个不确定的源可用,那么就会“更安全”。
std::random_devicestd::random_device

问题:

我的问题分为两部分:

  1. 用户空间
    • 有没有一种可移植的方法来检查当前平台上是否存在这种不确定的源?
  2. 编译器端
    • 如果主机平台没有可用的非确定性源,是否允许编译器用时间种子替换种子?或者标准中是否有某些东西会阻止这种替换?std::random_device
    • 如果标准没有禁止这种行为,那么编译器有什么理由不实现它呢?

也许这些应该是 2 或 3 个单独的问题。由于背景和问题很常见,我在同一篇文章中询问了他们关于该主题的完整性。如果无论如何我必须将它们分成单独的问题,请告诉我。

+11 C++ 语言律师 随机种子

评论

0赞 super 2/28/2020
只是出于好奇,你在用什么平台?
0赞 sweenish 2/28/2020
pcg-random.org/posts/cpp-seeding-surprises.html另一件需要考虑的事情。我认为,几乎所有的现代硬件都会有一个熵源。
0赞 Fareanor 2/28/2020
@super Windows 10 和 MinGW :p也许这是因为我是在调试模式下编译的。
0赞 super 2/28/2020
@Fareanor我在这台计算机上使用相同的配置,即 g++ 9.2.0,并且使用您的代码在每次运行时都会获得不同的序列。
1赞 super 2/28/2020
@Fareanor 不完全是。PRNG 的熵为 0,并且每次运行都不会产生相同的序列。这是一个旧的编译器,其实现不完整(错误),可能会给您带来问题。std::random_device

答:

3赞 NathanOliver 2/28/2020 #1

有没有一种可移植的方法来检查当前平台上是否存在这种不确定的源?

您可以使用 的熵成员函数来检查源是否是不确定的。不幸的是,它不是一个函数,因此您必须使用常规的 if 语句,例如std::random_devicestatic constexpr

int main()
{
    std::random_device rd;
    std::size_t seed;
    if (rd.entropy())
        seed = rd();
    else
        seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
    std::mt19937 gen(seed);
    std::uniform_int_distribution distr(0, 9);

    // Print a sequence of 10 uniformly distributed random integers
    for(std::size_t i = 0; i < 10; ++i)
        std::cout << distr(gen) << ' ';

    return 0;
}

如果主机平台没有可用的非确定性源,是否允许编译器用时间种子替换种子?或者标准中是否有某些东西会阻止这种替换?std::random_device

这将改变程序的可观察行为,所以不,编译器不能这样做。

评论

1赞 walnut 2/28/2020
我想 cppreference 页面上关于实现的注释很重要。std::random_device::entropy
0赞 Fareanor 2/28/2020
哎呀,我错过了文档中的这个成员函数......我感觉很散漫。谢谢你的回答。
2赞 phuclv 2/28/2020
注意:“此函数在某些标准库中未完全实现。例如,LLVM libc++ 始终返回零,即使设备是不确定的。相比之下,Microsoft Visual C++ 实现始终返回 32,而 boost.random 返回 10。