我听到一些人说,rand()即使在使用srand()获得种子后,使用也很糟糕。为什么呢?我想知道事情是如何发生的......还有另一个问题很抱歉......但是有什么替代方法呢?
bol*_*lov 15
这个故事有两个部分。
首先,rand是一个伪随机数生成器。这意味着它取决于种子。对于给定的种子,它总是给出相同的序列(假设实现相同)。这使得它不适合某些安全性非常重要的应用程序。但这并不是特定于rand. 这是任何伪随机生成器的问题。并且肯定有很多类别的问题可以接受伪随机生成器。真正的随机生成器有其自身的问题(效率、实现、熵),因此对于与安全无关的问题,最常使用伪随机生成器。
因此,您分析了您的问题并得出结论,伪随机生成器是解决方案。在这里,我们到达与C随机库中的真正的麻烦(包括rand和srand特定于它,让它)过时(又名:你应该原因从未使用rand以及C随机库)。
一个问题是它有一个全局状态(由 设置srand)。这使得无法同时使用多个随机引擎。它还极大地使多线程任务复杂化。
它最明显的问题是它缺少一个分发引擎:rand给你一个间隔的数字[0 RAND_MAX]。在这个区间是一致的,也就是说这个区间的每个数字出现的概率都是一样的。但大多数情况下,您需要一个特定时间间隔内的随机数。让我们说[0, 1017]。一个常用(和天真的)使用的公式是rand() % 1018. 但问题是,除非RAND_MAX是1018你的精确倍数,否则不会得到均匀分布。
另一个问题是rand. 这里还有其他答案比我更详细地说明了这一点,所以请阅读它们。
在现代 C++ 中,你绝对应该使用 C++ 库,<random>它带有多个随机定义良好的引擎以及整数和浮点类型的各种分布。
这里的答案都没有解释rand() 变坏的真正原因。
rand()是一个伪随机数生成器 (PRNG),但这并不意味着它一定是坏的。实际上,有非常好的 PRNG,它们在统计上很难或不可能与真正的随机数区分开来。
rand()是完全实现定义的,但从历史上看,它是作为线性同余生成器 (LCG) 实现的,这通常是一种快速但众所周知的糟糕的 PRNG 类。这些生成器的低位比高位具有低得多的统计随机性,并且生成的数字可以产生可见的晶格和/或平面结构(最好的例子是著名的RANDU PRNG)。一些实现尝试通过将位右移预定量来减少低位问题,但是这种解决方案也减少了输出的范围。
尽管如此,仍有一些优秀 LCG 的显着例子,例如 L'Ecuyer 的 64 位和 128 位乘法线性同余生成器,这些生成器在Tables of Linear Congruential Generators of different Sizes and Good Lattice Structure, Pierre L'Ecuyer, 1999 中介绍。
一般的经验法则是不要相信rand(),使用适合您需要和使用要求的自己的伪随机数生成器。
rand/ 的坏处srand是rand——
srand实现可重复的“随机性”。这两点加在一起,妨碍了实现改进 的实现的能力rand(例如,使用加密随机数生成器 [RNG] 或其他“更好”的算法来生成伪随机数)。例如,JavaScriptMath.random和 FreeBSDarc4random没有这个问题,因为它们不允许应用程序为可重复的“随机性”播种——正是出于这个原因,V8 JavaScript 引擎能够将其Math.random实现更改为xorshift128+while的变体保持向后兼容性。(另一方面,让应用程序提供额外的数据来补充“随机性”,如在 中BCryptGenRandom,问题较少;然而,即便如此,
还:
rand和srand是,即使再现的“随机性”并不之间保证未指定的装置rand/srand实施方式中,相同的标准库的不同版本之间,操作系统等之间srand在randis之前未调用,则rand其行为与srand(1)首次调用时类似。实际上,这意味着rand只能作为伪随机数生成器 (PRNG) 而不是作为非确定性 RNG 来实现,并且rand无论应用程序是否调用,PRNG 算法在给定的实现中都不会有所不同srand。编辑(2020 年 7 月 8 日):
还有一件更重要的事情是关于randand 的srand。这些函数的 C 标准中没有任何内容指定传递的“伪随机数”rand必须遵循的特定分布,包括均匀分布甚至近似均匀分布的分布。将此与 C++uniform_int_distribution和uniform_real_distribution类以及 C++ 指定的特定伪随机生成器算法(例如linear_congruential_engine和 )进行对比mt19937。
编辑(2020 年 12 月 12 日开始):
还有一个关于randand 的坏事srand:srand需要一个只能和unsigned. unsigned必须至少为 16 位,并且在大多数主流 C 实现中,unsigned是16 位或 32 位,具体取决于实现的数据模型(即使 C 实现采用 64 位数据模型,也不是 64 位)。因此,可以通过这种方式选择不超过 2^N 个不同的数字序列(其中 N 是 a 中的位数unsigned),即使 由 实现的底层算法rand可以产生更多不同的序列(例如, 2^128甚至 2^19937 就像在 C++ 中一样mt19937)。
| 归档时间: |
|
| 查看次数: |
5489 次 |
| 最近记录: |