我对 c++11 随机库有点困惑。
我的理解:我们需要两个独立的概念:
- 随机引擎,可以是:
- 伪(需要种子)又名 PRNG
- 真随机数发生器
- 分布:它将从引擎获得的数字映射到特定的间隔,使用特定的分布。
我不明白的是为什么不只使用真正的随机数生成器:
std::random_device rd;
std::uniform_int_distribution<int> dist(1, 5);
// get random numbers with:
dist(rd);
据我所知,这很好用。
相反,这是我在大多数示例/网站/文章中发现的:
std::random_device rd;
std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
std::uniform_int_distribution<int> dist{1, 5};
// get random numbers with:
dist(e);
我不是在谈论特殊用途,例如密码学,只是您的基本入门文章。
我的怀疑是因为 std::mt19937
(或 std::default_random_engine
)接受种子,通过在调试会话期间提供相同的种子可以更容易调试。
另外,为什么不只是:
std::mt19937 e{std::random_device{}()};
原文由 bolov 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果您只执行一次可能会很好,但如果您会执行多次,最好跟踪您的
std::random_device
而不是不必要地创建/销毁它。查看 libc++ 源代码以实现
std::random_device
可能会有所帮助,这非常简单。它只是std::fopen("/dev/urandom")
上的一个薄包装。因此,每次创建std::random_device
时,您都会获得另一个文件系统句柄,并支付所有相关费用。据我了解,在 Windows 上,
std::random_device
代表对微软加密 API 的一些调用,因此每次执行此操作时都将初始化和销毁一些加密库接口。这取决于您的应用程序,但出于一般目的,我不会认为这种开销总是可以忽略不计。有时是这样,然后这很棒。
我想这与您的第一个问题有关:
至少我的想法是:
std::mt19937
是一个非常简单可靠的随机发生器。它是独立的,将完全存在于您的进程中,而不需要调用操作系统或其他任何东西。该实现是由标准 强制执行 的,至少在 boost 中,它在任何地方都使用相同的代码,源自原始的mt19937
论文。这段代码非常稳定并且是跨平台的。您可以非常自信地初始化它、从中查询等将在您编译它的任何平台上编译成类似的代码,并且您将获得类似的性能。std::random_device
相比之下非常不透明。你并不确切知道它是什么,它会做什么,或者它的效率如何。你甚至不知道它是否真的可以被获取——当你尝试创建它时它可能会抛出一个异常。你知道它不需要种子。您通常不应该从中提取大量数据,只需使用它来生成种子。有时,它作为加密 API 的一个很好的接口,但实际上并不需要这样做,遗憾的是有时它不需要。它可能对应于/dev/random
在 unix 上,它可能对应于/dev/urandom/
。它可能对应于一些 MSVC 加密 API (visual studio),或者它可能只是一个固定常量 (mingw)。如果您为某些手机进行交叉编译,谁知道它会做什么。 (即使你得到/dev/random
,你仍然会遇到性能可能 不一致 的问题——它可能看起来工作得很好,直到熵池用完,然后它像狗一样慢.)我的想法是,
std::random_device
应该是一种改进版本的播种time(NULL)
这是一个低标准,因为time(NULL)
考虑到所有事情,种子都很糟糕。我通常在过去使用time(NULL)
生成种子的地方使用它。除此之外,我真的不认为它有那么有用。