【问题标题】:How to initialize std::mt19937 when std::random_device entropy is unknown? (VS C++/Windows)当 std::random_device 熵未知时如何初始化 std::mt19937? (VS C++/Windows)
【发布时间】:2020-09-03 16:27:15
【问题描述】:

我正在尝试构建一个简单的随机数生成器,但我想确保 random_device 正常工作。我从以下代码开始:

#include <random>
#include <chrono>

class Generator {
public:
    Generator()
        :
        m_DeviceSeed(rd()),
        m_TimeSeed(std::chrono::high_resolution_clock::now().time_since_epoch().count()),
        rng(m_DeviceSeed)
    {
        if (rd.entropy() == 0.0) {
            rng.seed((unsigned)m_TimeSeed);
        }
    }
private:
    //Vars
    std::random_device rd;
    unsigned int m_DeviceSeed;
    unsigned long long m_TimeSeed;
    std::mt19937 rng;
};

我曾看到“std::chrono::high_resolution_clock::now().time_since_epoch().count()”被推荐为 random_device 的替代品,我认为检查熵可以让我将其用作后备;但是,这是用 Visual Studio 编写的,显然这意味着熵总是显示 32,无论它是否为真。

那么,我的问题是这样的:在没有熵测试手段的情况下播种 std::mt19937 的最稳健方法是什么? chrono 更好还是 random_device 更好?还是某种组合,或完全是其他选择?

基于此:The implementation of random_device in VS2010?

在大多数情况下,random_device 似乎是播种或生成种子序列的安全选择,但我想确定一下。

【问题讨论】:

  • 使用当前纪元?
  • 如果你不能依赖熵,那就不要使用它。您的备份计划应该足以在所有情况下使用。
  • 注意,在m_DeviceSeed的构造器初始化列表项中,rd还没有被构造,所以rd()不一定做任何有意义的事情。
  • “所以,我的问题是:在没有熵测试手段的情况下,最稳健的播种 std::mt19937 的方法是什么?” - 我会使用std::seed_seq 进行播种,该std::seed_seq 由一些相当随机的东西构成,例如当前时间(尽可能精确)、当前进程ID、内存中main 的地址、用户ID当前用户等,然后填写来自std::random_device 的输出。
  • 一种方法是正确编写代码。惠特:m_DeviceSeed(rd()) 这是 UB。 rd 尚未初始化,因为成员是按声明顺序初始化的。

标签: c++ random visual-c++ c++17 mt19937


【解决方案1】:

下班后我花了一些时间深入研究 Microsoft 文档和 VS 源代码,我想我应该分享一下!标题是这样的:

_NODISCARD double entropy() const noexcept
    {   // return entropy of random number source
    return (32.0);
    }

_NODISCARD result_type operator()()
    {   // return next value
    return (_Random_device());
    }

第一部分,看起来熵毕竟总是32.0,操作员调用_Random_Device。我能够将该定义追踪到使用 rand_s 的 xrngdev.cpp。

_CRTIMP2_PURE unsigned int __CLRCALL_PURE_OR_CDECL _Random_device()
    {   // return a random value
    unsigned int ans;
    if (_CSTD rand_s(&ans))
        _Xout_of_range("invalid random_device value");
    return (ans);
    }

根据 Microsoft 文档,“rand_s 函数将 0 到 UINT_MAX 范围内的伪随机整数写入输入指针。rand_s 函数使用操作系统生成加密安全随机数。”

此外,追踪 rand_s.cpp 显示:

    if (!__acrt_RtlGenRandom(result, static_cast<ULONG>(sizeof(*result))))
{
    errno = ENOMEM;
    return errno;
}

确认它使用 RtlGenRandom,如下所示:The implementation of random_device in VS2010?

RtlGenRandom 有点像一个黑盒子,但从发布的内容来看,它使用:

[RtlGenRandom] 按照 FIPS 186-2 附录 3.1 的规定生成 SHA-1 作为 G 函数。熵来自:

当前进程 ID (GetCurrentProcessID)。

当前线程 ID (GetCurrentThreadID)。

自启动以来的滴答计数 (GetTickCount)。

当前时间 (GetLocalTime)。

各种高精度性能计数器(QueryPerformanceCounter)。

用户环境块的 MD4 哈希,包括用户名、计算机名和搜索路径。 [...]

高精度内部CPU计数器,如RDTSC、RDMSR、RDPMC

[省略:低级系统信息字段和性能计数器的长列表] [4]

是的,std::random_device 似乎是几乎所有 Windows XP 之后的 Windows 应用程序中最强大的选项。

虽然我仍将遵循提供的 cmets 并创建一个 get_seed() 样式函数来生成带有一些额外曲折的完整 std::seed_seq,但这至少回答了我的问题,即 std::random_device 可能是更强大的“基础” " 播种选项。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-17
    • 2015-04-08
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 2013-09-12
    • 2018-01-13
    相关资源
    最近更新 更多