C++ 为什么不直接使用随机设备呢?

C++ 为什么不直接使用随机设备呢?,c++,c++11,random,C++,C++11,Random,我对c++11随机库有点困惑 我的理解:我们需要两个独立的概念: 随机引擎(可以是伪引擎(需要种子)也可以是实引擎) 分布:它使用特定分布将从引擎获得的数字映射到特定间隔 我不明白的是为什么不直接用这个: std::random_device rd; std::uniform_int_distribution<int> dist(1, 5); // get random numbers with: dist(rd); 这是一个很好的起点 我将综合几点: 它的成本不得而知。

我对c++11随机库有点困惑

我的理解:我们需要两个独立的概念:

  • 随机引擎(可以是伪引擎(需要种子)也可以是实引擎)
  • 分布:它使用特定分布将从引擎获得的数字映射到特定间隔
我不明白的是为什么不直接用这个:

std::random_device rd;
std::uniform_int_distribution<int> dist(1, 5);

// get random numbers with:
dist(rd);
这是一个很好的起点

我将综合几点:

  • 它的成本不得而知。 从这个“设备”读取一个数字的成本有多高?这是未指明的。例如,它可以在Linux系统上读取/dev/random,这可能会阻塞很长一段时间等待熵(熵本身由于各种原因存在问题)

根据我个人的经验,我已经注意到,
std::random\u设备
通常比简单的伪随机算法慢。一般来说,这可能不是真的,但通常是真的。这是因为它可能涉及物理设备,或简单CPU以外的其他硬件

  • 它实际上可能是确定性的。 C++11的std::random_设备不需要是非确定性的!实现可以并且确实将其实现为具有固定种子的简单RNG,因此它为程序的每次运行生成相同的输出

还有,为什么不:

std::mt19937 e{std::random_device{}()};
std::mt19937 e{std::random_设备{}()}

如果您只做一次可能没问题,但是如果您要做很多次,最好跟踪您的
std::random_设备
,不要不必要地创建/销毁它

查看libc++源代码以实现
std::random_device
,可能会有所帮助,这非常简单。它只是std::fopen(“/dev/uradom”)
上的一个薄包装。因此,每次创建一个
std::random_设备时,您都会得到另一个文件系统句柄,并支付所有相关费用

据我所知,在windows上,
std::random_device
表示对microsoft crypto API的一些调用,因此每次执行此操作时,您都将初始化并销毁一些加密库接口

这取决于您的应用程序,但出于一般目的,我不认为这种开销总是可以忽略不计的。有时是这样,然后这是伟大的

我想这与你的第一个问题有关:

相反,这是我在大多数示例/站点/文章中发现的:

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::随机设备rd;
std::mt19937 e{rd()};//或者std::default_random_引擎e{rd()};
std::均匀分布区{1,5};
至少我的想法是:

  • std::mt19937
    是一种非常简单可靠的随机发生器。它是自包含的,将完全存在于您的流程中,而不会调用操作系统或其他任何东西。该实现是由标准强制执行的,至少在boost中,它在任何地方都使用相同的代码,这些代码源自原始的
    mt19937
    论文。这段代码非常稳定,并且是跨平台的。您可以非常确信,在编译它的任何平台上,初始化它、从它进行查询等都将编译成类似的代码,并且您将获得类似的性能

  • 相比之下,随机设备是相当不透明的。你不知道它到底是什么,它将要做什么,或者它的效率有多高。你甚至不知道它是否真的可以被获取——当你试图创建它时,它可能会抛出一个异常。你知道它不需要种子。您通常不需要从中提取大量数据,只需使用它生成种子即可。有时,它充当加密API的一个很好的接口,但实际上并不需要这样做,不幸的是,有时它不是。它可能对应于unix上的
    /dev/random
    ,也可能对应于
    /dev/urandom/
    。它可能对应于某些MSVC加密API(visual studio),也可能只是一个固定常量(mingw)。如果你交叉编译一些手机,谁知道它会做什么。(即使你得到了
    /dev/random
    ,你仍然有一个问题,那就是性能可能不一致——它可能看起来工作得很好,直到熵池耗尽,然后它像狗一样慢。)


我的想法是,
std::random_device
应该像一个改进版的播种时间(NULL)
——这是一个低条,因为时间(NULL)是一个非常糟糕的种子。我通常在当天使用
time(NULL)
生成种子的地方使用它。我真的不认为它在外面有用。

< Po> Okdoki,让我们澄清一些混淆吧!

<代码> STD::RealthMyPosivs/Cuff>是标准的C++,相当于Time>(null)。在C.,标准通常建议您只使用它来播种,原因如下:

一旦熵池耗尽,随机_设备的许多实现的性能就会急剧下降。对于实际使用,随机_装置通常仅用于对PRNG(如mt19937)进行播种

(访问查看他们的措辞)

然而,这种推理并不完全准确。该标准还规定(从)如果依赖于系统的CSPRNG不可用,则允许
std::random_设备的实现具有确定性:

26.5.6等级随机_装置

2如果实现限制阻止生成非确定性随机数,则实现可采用随机数引擎

因此,
std::random_device
可能永远不会与熵池交互,因此
entropy
成员函数可能返回零:

26.5.6等级随机_装置

双熵()常数不例外

5返回:如果实现使用随机数eng
 std::random_device rd;
 std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
 std::uniform_int_distribution<int> dist{1, 5};
std::mt19937 e{std::random_device{}()};
std::mt19937 e{std::random_device{}};