【问题标题】:C++ randomizer - first random number always a factor of 7C++ 随机器 - 第一个随机数始终是 7 的因数
【发布时间】:2018-11-12 17:17:30
【问题描述】:

我有一个简单的 C++ 程序,用来与我的学生演示随机选择数组元素。

像往常一样,我使用srand (time (0)); 播种,然后使用rand() 生成数字。

数组有七个条目,选择的条目是第一个 - 下标 0。

我现在已经在 40 分钟内运行了该程序的 15 次版本,第一个随机生成的数字是 7 的倍数,例如:

1258771276 1258586399 1229409447 1257140997 1256216612 1260855344 1262973026 1266351233

我将程序更改为在播种后选择五个随机数,并且(在生成 7 的初始倍数后),后续数字表现得更好。

我了解“随机”意味着我无法预测数字会有所不同,就像我无法预测它们相同。

我也明白这些是伪随机数。

我也明白 rand() 预计不会是顶级加密随机化。

不过,这种行为太奇怪了,我完全惊呆了。我是否陷入了 C++ 运行时的奇怪裂缝?这是在完全修补到 Mojave 10.14.1 的 MacOS 上使用 Xcode。

【问题讨论】:

  • 像 rand() 的(通常)实现这样的线性同余生成器通常会显示这样的伪像 - 这就是为什么您不应该将它们用于任何严肃的应用程序的原因。另一个伪像似乎是生成的数字的前两位始终是 12。如果您必须使用 rand(),那么取中间数字通常可以为您提供“更好”的值。
  • 也许这个stackoverflow.com/questions/13445688/…会有所帮助。
  • 我同意 Neil 的观点,如果我可以补充的话,C++ rand() 函数非常方便(并且可能作为教师最容易演示),但已被更完整的库函数所取代, cplusplus.com/reference/random
  • 看到一个伪随机生成器在播种后总是生成 7 的倍数,但对于序列中的进一步伪随机值没有显示这种异常,这将是相当奇怪的。毕竟,典型的rand 实现通常不会从“继承自先前rand() 调用”的状态中辨别出“新播种”状态。如果您观察到类似的情况,我会怀疑种子值存在异常,而不是生成器实现中的异常。但是,rand() 实现中的错误仍然可能导致此问题。
  • @AnT:不幸的是,这个生成器就是这种情况。它生成的第一个数字几乎总是具有相同的值(mod 16807)。并且 16807 可以被 7 整除。这就是 OP 遇到这种情况的原因。

标签: c++ random


【解决方案1】:

这里是随机生成器的源代码:

static int
do_rand(unsigned long *ctx)
{
/*
 * Compute x = (7^5 * x) mod (2^31 - 1)
 * without overflowing 31 bits:
 *      (2^31 - 1) = 127773 * (7^5) + 2836
 * From "Random number generators: good ones are hard to find",
 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
 * October 1988, p. 1195.
 */
    long hi, lo, x;

    /* Can't be initialized with 0, so use another value. */
    if (*ctx == 0)
        *ctx = 123459876;
    hi = *ctx / 127773;
    lo = *ctx % 127773;
    x = 16807 * lo - 2836 * hi;
    if (x < 0)
        x += 0x7fffffff;
    return ((*ctx = x) % ((unsigned long)0x7fffffff + 1));
}

考虑到当前最先进的生成器,这不是一个高质量的生成器。不过,也没有那么糟糕。

但是,它的种子机制是最糟糕的:它只是将ctx 设置为种子。因此,如果您将种子设置为相似的值(就像您的示例一样,随时间播种,这意味着它将具有相似的值),它生成的下一个数字将与种子具有非常强的相关性。

该问题的一个解决方案是在播种后生成“一些”数字。然而,它并不能完美地解决问题(通常,种子与生成的数字有很强的相关性,即使在非常很长的距离上也是如此)。

或者,更好的解决方案是忘记rand(),并使用更现代(也更难使用)的随机, 这是#include &lt;random&gt;。 查看 cppreference 的 documentation 了解它。

【讨论】:

  • 一个快速的实验表明,这个 RNG 产生的第一个结果可以被 7 整除,大约 14.3% 的种子值,正如预期的那样。这是完全正常的,甚至不接近 OP 的说法。
  • @AnT: 什么是不正常的,如果种子值彼此接近,那么第一个结果将是相同的(mod 16807)。
  • 第一个结果相同(mod 16807)与第一个结果被 7 整除不同。
  • @AnT:在 OP 尝试这个的时候,它的意思是一样的。此生成器为彼此相邻的 127773 个种子提供相同的值(mod 16807)。这意味着 1.5 天(如果我们以秒计算)。这意味着,在 1.5 天内,使用time() 播种,它将给出相同的第一个数字(mod 16807)。或相同的数字(mod 7),因为 16807 可以被 7 整除。对于 OP,这个相同的数字恰好为零。但是如果 OP 明天尝试这个,OP 可能会得到 2。1.5 天。
  • 显然你是对的。但这只是使用播种技术而不是仅使用流行的srand(time(0)) 的另一个原因(假设应用程序需要更加小心)。在大多数应用程序中,这并不重要。坦率地说,我很难想出一个既能满足现有 LCG 发生器的应用程序,又担心这种播种行为。
猜你喜欢
  • 2012-04-12
  • 2012-01-25
  • 2011-11-07
  • 1970-01-01
  • 2018-04-06
  • 2014-04-27
  • 2022-11-21
  • 1970-01-01
  • 2015-09-10
相关资源
最近更新 更多