【问题标题】:Understanding the algorithm of Visual C++'s rand() function了解Visual C++的rand()函数的算法
【发布时间】:2011-07-22 16:27:16
【问题描述】:

在 C/C++ 中,rand()srand() 通常被我们用来获取随机整数。但是当我尝试自己重写时,发现算法很难理解。函数很简单,几行就写好了,但是公式有误。

主要公式:

ptd->_holdrand = ptd->_holdrand * 214013L + 2531011L;

涉及的原始代码:

void __cdecl srand (unsigned int seed)
{
    _getptd()->_holdrand = (unsigned long)seed;
}

int __cdecl rand (void)
{
    _ptiddata ptd = _getptd();
    return ( ((ptd->_holdrand = ptd->_holdrand * 214013L + 2531011L) >> 16) & 0x7fff );
}

【问题讨论】:

  • 嗯,我不知道,这个功能已经在那里了......为什么要尝试重新实现它?
  • @Rocky,尝试理解我们认为理所当然的代码基础并没有错。事实上,它应该被鼓励。
  • @Rocky:确实!如果你至少没有机会解释它的原理,就不要认为这是理所当然的。郭启:如果你厌倦了 LCG,可以看看 Mersenne Twister,一款流行、快速、高质量的 PRNG。
  • @Mark 谢谢你的鼓励:)我完全同意你的看法。
  • @Kerrek 谢谢你的回答,我试试看。

标签: c++ c algorithm math


【解决方案1】:

这只是模运算。您将乘以并添加到取模 2^32 的数字(例如),并将高 16 位返回为您的“随机”数字。因为您正在将与模数互质的数字相乘和相加,所以这会创建一种均匀分布的数字。

仔细选择这两个数字非常重要。例如,如果您使用了“* 4”和“+ 8”,您可能不会遇到很多随机性。

这个方案叫做linear congruential

【讨论】:

    【解决方案2】:

    那个伪随机数生成器是Linear Congruential Generator

    【讨论】:

      【解决方案3】:

      您可以在本月 (7-2011) 发表于 Dr. Dobb's Journal 的一篇优秀文章中找到对线性同余生成器 (LCG) 和其他类似家族或伪随机生成器的解释,以及关于这些特定常数的选择的说明(DDJ):Fast, High-Quality, Parallel Random-Number Generators: Comparing Implementations

      我认为你需要在 DDJ 的网站上注册(免费)才能阅读本文的第一部分 (link),但如果你喜欢 C++ 和数学,无论如何你都应该这样做......

      【讨论】:

      • 谢谢:)我已经进入了给定的链接,但是由于英语不是我的母语,所以很难阅读。我会尽力而为。
      【解决方案4】:

      在调用 rand 之前,由于 srand 的手册页指出“如果没有提供种子值,则 rand() 函数会自动以 1 的值作为种子”,那么调用 rand 的更好方法是首先调用srand 将“将其参数设置为由 rand() 返回的新伪随机整数序列的种子”。

      例如,考虑以下 awk、nawk、gawk 代码,该代码在 bash shell 脚本中用于创建新的(随机)mac 地址 - 即代码 sn-p 中引用的 .genmacaddr:

      enter code here
      BEGIN {
           n0 = "00"
           srand()
           n1 = sprintf("%02x", int(255 * rand()))
           n2 = sprintf("%02x", int(255 * rand()))
           n3 = sprintf("%02x", int(255 * rand()))
           n4 = sprintf("%02x", int(255 * rand()))
           n5 = sprintf("%02x", int(255 * rand()))
           print n0":"n1":"n2":"n3":"n4":"n5
      }
      

      bash shell 脚本中的代码 sn-p 在哪里:

      enter code here
      ifconfig eth0 down
      newmacaddr=`nawk -f .genmacaddr -`
      ifconfig eth0 hw ether $newmacaddr
      ifconfig eth0 up
      

      如果我没记错的话,srand 的种子值来自系统时钟。

      我希望这可以帮助您了解一种可行的编码解决方案。

      【讨论】:

        【解决方案5】:

        正如已经说过的那样,它是一个线性同余,(如果您想深入了解它们如何生成伪随机值,可以查看它)

        种子存储在_getptd()->_holdrand(以下称为holdrand)中

        此代码通过执行正常的乘法和加法步骤“起作用”,但随后将 holdrand 溢出到 得到隐含的“模数”0x100000000。

        我提到这一点是因为它不是很明显,而且通常不被认为是好的风格。

        从技术上讲,整数变量溢出不会引发未定义的行为,但在大多数平台上它是完全可以预测的,因此微软的工程师忽略了这个问题。

        【讨论】:

          猜你喜欢
          • 2010-11-04
          • 2014-11-05
          • 1970-01-01
          • 2012-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多