【问题标题】:rand() not giving me a random number (even when srand() is used)rand() 没有给我一个随机数(即使使用 srand())
【发布时间】:2015-01-12 14:04:25
【问题描述】:

好吧,我开始失去理智了。我想做的只是随机一个 0 到 410 之间的数字,根据this 页面,我的代码应该这样做。而且由于我想要一个随机数而不是伪随机数,所以我也在使用 srand() ,例如this 线程告诉我这样做。但这行不通。我得到的只是一个数字,取决于我上次执行以来的时间。如果我例如尽可能快地再次执行它,该数字通常比最后一个数字高 6 个数字,如果我等待更长的时间,它会更高,等等。当它达到 410 时,它会回到 0 并重新开始。我错过了什么?

编辑:哦,如果我删除 srand(time(NULL)); 行,我每次运行程序时都会得到相同的数字 (41)。这甚至不是伪随机数,这只是一个静态数字。只是从the article I linked to above 复制第一行代码仍然给我41 号。我是《23 号》续集中的明星,还是我错过了什么?

int main(void) {

    srand(time(NULL));
    int number = rand() % 410;

    std::cout << number << std::endl;

    system("pause");

}

【问题讨论】:

  • want a random number and not a pseudo-random number - 不,srand 只保证返回不同的值。序列仍然是伪随机的
  • 你已经被 Lavavejed 了!请参阅:channel9.msdn.com/Events/GoingNative/2013/… -- 具体来说,您的确切问题将在 3:30 分钟讨论。
  • “随机数而不是伪随机数”是什么意思?如果您只是想要一个与种子没有明显关联的数字,那么在使用它的值之前调用rand() 一次或多次;听起来您对 rand 的实现相当差,它将种子作为第一个值返回。如果您真的需要一个“真正的”随机数,那么您无法仅从软件中获得它。 std::random_device 可以给你,但前提是有合适的硬件设备供它使用。

标签: c++ random srand


【解决方案1】:

这就是使用已弃用的随机数生成所获得的结果。

rand 产生一个固定的数字序列(这本身很好),而且做得非常非常糟糕。

您通过srand 告诉rand 序列从哪里开始。由于您的“起点”(称为 seed btw)取决于自 1.1.1970 0:00:00 UTC 以来的秒数,因此您的输出显然取决于时间。

做你想做的事的正确方法是使用 C++11 &lt;random&gt; 库。在您的具体示例中,这看起来有点像这样:

std::mt19937 rng (std::random_device{}());
std::uniform_int_distribution<> dist (0, 409);

auto random_number = dist(rng);

有关rand 的弊端和&lt;random&gt; 的优点的更多信息,请查看this

最后要说的是,像我在上面所做的那样播种std::mt19937 并不是很理想,因为MT 的状态空间远大于单次调用std::random_device{}() 得到的32 位空间。这对于玩具程序和您的标准学校作业来说不是问题,但仅供参考:Here 是我对 MT 整个状态空间的播种,以及答案中的一些有用建议。

【讨论】:

    【解决方案2】:

    来自手册:

    time() 以自 Epoch 以来的 秒数 形式返回时间, 1970-01-01 00:00:00 +0000 (UTC)。

    这意味着如果您在同一秒内两次启动程序,您将使用相同的值初始化 srand 并获得相同的 PRNG 状态。

    如果您通过调用srand 删除初始化,您将始终从rand 获得完全相同的数字序列。

    【讨论】:

      【解决方案3】:

      恐怕你不能在那里得到真正的随机数。内置函数旨在提供伪随机数。此外使用 srand 和 rand,因为第一个使用与第二个相同的方法。如果你想制作真正的随机数,你必须找到一个正确的熵源,例如处理大气噪声,就像 www.random.org 的方法一样。

      这里的问题在于随机算法使用的种子:如果它是一个机器提供的数字,它不可能是不可预测的。一个正常的解决方案是使用外部硬件。

      【讨论】:

        【解决方案4】:

        很遗憾,您无法从没有特定硬件的计算机上获得真正的随机数(这通常太慢而无法实用)。

        因此,您需要使用伪生成器制作。但是你需要小心使用它们。

        函数rand 旨在返回一个介于 0 和 RAND_MAX 之间的数字,从广义上讲,它满足均匀分布的统计特性。充其量你可以期望抽取数字的平均值为0.5 * RAND_MAX,方差为RAND_MAX * RAND_MAX / 12

        rand 的典型实现是一个线性同余生成器,这基本上意味着返回的数字是前一个数字的函数。这可以产生令人惊讶的好结果,并允许您使用srand 函数种子生成器。

        但是 重复使用srand会破坏生成器的统计特性,这就是发生在你身上的事情:你对srand的使用与你的系统时钟时间。您观察到的行为完全符合预期。

        您应该做的是只调用一次srand,然后使用rand 绘制一个数字序列。您不能以您设置的方式轻松地做到这一点。但是还有其他选择;你可以切换到一个随机数生成器(比如 mersenne twister),它允许你绘制第(n)项,你可以将 n 的值作为命令行参数传递。

        最后,我会避免在绘制数字时使用模数。如果您的模不是 RAND_MAX 的倍数,这将产生统计偏差。

        【讨论】:

          【解决方案5】:

          尝试通过 time(0) 更改 time(NULL) 中的 NULL(这将为您提供当前系统时间)。如果不行,你可以尝试通过time(0)*1000将time(0)转换成ms。

          【讨论】:

          • 谢谢,但我没有工作。将其更改为 time(0) 没有任何效果,而将其更改为 time(0)*1000 会将错误更改为类似的错误,但数字是递减的。
          • NULL0 都具有相同的值,在这种情况下没有区别。当将秒数用作随机种子时,将秒数乘以一个常数没有任何帮助。
          猜你喜欢
          • 1970-01-01
          • 2020-07-13
          • 2021-10-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-12
          相关资源
          最近更新 更多