如何以安全且便携的方式为随机数生成器提供种子?

Far*_*nor 5 c++ language-lawyer random-seed

背景:

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

std::mt199937 gen(std::chrono::high_resolution_clock::now().time_since_epoch().count()); // time seed
Run Code Online (Sandbox Code Playgroud)

问题:

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

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

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

问题:

我的问题分为两部分:

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

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

Nat*_*ica 3

是否有一种可移植的方法来检查当前平台上是否存在此类非确定性源?

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 注意:*“此函数在某些标准库中并未完全实现。例如,即使设备是不确定的,LLVM libc++ 也始终返回零。相比之下,Microsoft Visual C++ 实现始终返回 32,而 boost.random 返回 10。 “* (2认同)