c++ 为什么使用兰德()被认为是不好的?

vxbzzdmp  于 12个月前  发布在  其他
关注(0)|答案(7)|浏览(134)

此问题在此处已有答案

Why is the new random library better than std::rand()?(4个答案)
Why do people say there is modulo bias when using a random number generator?(10个答案)
2天前关闭。
尽管通过srand()使用种子,但rand()的使用通常是不受欢迎的。为什么会这样?有什么更好的替代方案?

lo8azlld

lo8azlld1#

这个故事有两个部分。

伪随机数生成器

第一、rand是一个pseudorandom number generator。这意味着它依赖于一个种子。对于一个给定的种子,它总是给出相同的序列(假设实现相同)。这使得它不适用于安全性非常重要的某些应用程序。但是这并不特定于rand。这是任何伪随机生成器。当然,有很多类问题都可以使用伪随机生成器。真正的随机生成器有其自身的问题(效率,实现,熵),因此对于与安全无关的问题,通常使用伪随机生成器。

C随机库的缺点和实现质量

所以你分析了你的问题,你得出结论,伪随机发生器是解决方案。在这里,我们到达了C随机库(包括randsrand)的真实的问题,这些问题是特定于它的,并使它过时(也就是:你应该**永远 * 使用rand和C随机库的原因)。

  • 一个问题是它有一个全局状态(由srand设置)。这使得它不可能同时使用多个随机引擎。它也极大地复杂化了多线程任务。
  • 它最明显的问题是它缺乏分发引擎rand给出了一个在[0 RAND_MAX]区间内的数。它在这个区间内是均匀的,这意味着这个区间内的每个数字出现的概率都是相同的。但大多数情况下,你需要一个特定区间内的随机数。假设[0, 1017]。A通常(而且很简单)使用的公式是rand() % 1018。但问题是,除非RAND_MAX1018的精确倍数,否则你不会得到均匀分布。
  • 另一个问题是rand的实现质量。这里有其他答案比我更好地阐述这个问题,所以请阅读它们。

C++

在现代C中,你绝对应该使用来自<random>的C库,它带有多个随机的定义良好的引擎和各种整数和浮点类型的发行版。

zour9fqk

zour9fqk2#

这里没有一个答案解释了rand() * 坏 * 的真实的原因。
rand()是一个pseudo-random number generator (PRNG),但这并不意味着它一定是坏的。实际上,有非常好的PRNG,统计上很难或不可能从真正的随机数中区分出来。
rand()是完全实现定义的,但历史上它被实现为Linear Congruential Generator (LCG),这通常是一个快速的,这些生成器的低位具有比高位低得多的统计随机性,并且生成的数字可以产生可见的晶格和/或平面结构(最好的例子是著名的RANDU PRNG)。一些实现试图通过将位向右移动预定义的量来减少低位问题,然而这种解决方案也减少了输出的范围。
尽管如此,仍有优秀的LCG的显著例子,如L 'Ecuyer的64位和128位乘法线性同余生成器,其在Pierre L' Ecuyer,1999年的 *Tables of Linear Congruential Generators of Different Sizes and Good Lattice Structure * 中提出。
一般的经验法则是不要相信rand(),使用适合您的需求和使用要求的伪随机数生成器。

9rygscc1

9rygscc13#

rand/srand的缺点是rand-

  • 对它生成的数字序列使用未指定的算法,
  • 允许使用srand初始化该算法以获得可重复的“随机性”。

这两点加在一起,妨碍了实现对rand实现的改进。(例如,使用加密随机数生成器[RNG]或其他“更好”的算法来产生伪随机数)。例如,JavaScript的Math.random和FreeBSD的arc4random就没有这个问题,因为它们不允许应用程序为它们提供可重复的“随机性”-正是由于这个原因,V8 JavaScript引擎能够将其Math.random实现更改为xorshift128+的变体,同时保持向后兼容性。(另一方面,让应用程序提供额外的数据来 * 补充 *“随机性”,如BCryptGenRandom,问题不大;然而,即使如此,这通常也只在加密RNG中看到。
还有:

  • randsrand的算法和播种过程是未指定的,这意味着即使在rand/srand实现之间、between versions of the same standard library之间、操作系统之间等也不能保证可再现的“随机性”。
  • 如果在rand之前没有调用srand,则rand的行为类似于srand(1)第一次被调用。实际上,这意味着rand只能实现为伪随机数生成器(PRNG),而不是非确定性RNG,并且rand的PRNG算法在给定的实现中不能不同,无论应用程序是否调用srand

编辑(2020年7月8日):
randsrand还有一个更重要的缺点。这些函数的C标准中没有规定rand提供的“伪随机数”必须遵循的特定分布,包括均匀分布,甚至是近似均匀分布的分布。的uniform_int_distributionuniform_real_distribution类,以及C指定的特定伪随机生成器算法,如linear_congruential_enginemt19937
编辑(2020年12月12日开始):
randsrand的另一个缺点是:unsigned必须至少为16位,在大多数主流C实现中,unsignedeither 16 or 32 bits depending on the implementation's data model(值得注意的是,即使C实现采用64位数据模型,也不是64位)。(其中N是unsigned中的位数),即使rand实现的底层算法可以产生比这更多的不同序列(比如,像C
mt19937中那样,2^128甚至2^19937)。

wn9m85ua

wn9m85ua4#

首先,srand()不会得到一个种子,它设置了一个种子。种子是任何伪随机数生成器(PRNG)使用的一部分。当种子时,PRNG从该种子产生的数字序列是严格确定的,因为(大多数?)计算机没有办法产生真正的随机数。改变你的PRNG不会阻止序列从种子开始重复,事实上,这是一件好事,因为产生相同伪随机数序列的能力通常是有用的。
那么,如果所有的PRNG都与rand()共享这个特性,为什么rand()被认为是坏的呢?好吧,这归结为伪随机的“伪”部分。我们知道PRNG不可能是真正的随机数,但我们希望它尽可能接近真正的随机数生成器,并且存在various tests,其可以被应用于检查PRNG序列与真随机序列的相似程度。尽管其实现未被标准指定,rand()在每一个常用的编译器中使用一种非常旧的适合于非常弱的硬件的生成方法,而且它在这些测试中产生的结果相当差。由于这个时候已经创建了许多更好的随机数生成器,最好选择一个适合您的需求,而不是依赖于质量差的随机数生成器。由rand()提供。
哪一个适合你的目的取决于你正在做什么,例如,你可能需要加密质量,或多维生成,但对于许多用途,你只是希望事情是相当均匀的随机,快速生成,而且钱也不重要,这取决于你可能想要的xoroshiro128+生成器的结果的质量。的<random>头,但提供的发电机不是最先进的,现在有更好的,但是,他们仍然足够好,为大多数目的和相当方便。
如果钱在线上(例如,在网上赌场 Shuffle 等),或者你需要加密的质量,你需要仔细调查适当的发电机,并确保它们完全符合你的特定需求。

nqwrtyyt

nqwrtyyt5#

由于历史原因,rand通常(但并不总是)是一个非常糟糕的pseudo-random number generator(PRNG)。它有多糟糕取决于实现。
C++11有很好的,更好的PRNG。使用它的<random> standard header。特别是std::uniform_int_distributionhere,它在std::mersenne_twister_engine上面有一个很好的例子。
PRNG是一个非常棘手的问题。我对它们一无所知,但我相信Maven。

fcg9iug3

fcg9iug36#

让我补充另一个使兰德()完全不可用的原因:标准没有定义它生成的随机数的任何特征,既没有分布也没有范围。
没有分布的定义,我们甚至不能 Package 它,以获得我们想要的分布。
更进一步,理论上,我可以通过简单地返回0来实现兰德(),并声明我的rand()的RAND_MAX为0。
或者更糟糕的是,我可以让最低有效位总是0,这并不违反标准。
实际上,兰德()是实现定义的,标准说:
对于产生的随机序列的质量没有保证,并且已知某些实现产生具有令人不安的非随机低阶位的序列。具有特殊要求的应用应使用已知足以满足其需要的生成器
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf p36

qxsslcnc

qxsslcnc7#

如果你使用兰德(),你在生成你的随机数之后基本上会得到相同的结果。所以即使在使用srand()之后,如果有人能猜到你使用的种子,那么预测生成的数字也会很容易。这是因为函数rand()使用特定的算法来生成这样的数字
有一些时间可以浪费,您可以找出如何在给定种子的情况下预测函数生成的数字。现在您所需要的就是猜测种子。有些人将种子称为当前时间。因此,如果可以猜测您运行应用程序的时间,我就能够预测数字
使用兰德()是不好的!

相关问题