理解Visual C ++的rand()函数的算法

在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 ); } 

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

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

该方案称为线性同余 。

该伪随机数发生器是线性同余发生器 。

您可以在本月(7-2011)在Dobb博士期刊(DDJ)上发表的优秀文章中找到线性同余发生器(LCG)和其他类似系列或伪随机发生器的解释以及这些特定常数的选择。 : 快速,高质量,并行随机数发生器:比较实现 。

我想你需要在DDJ的网站上注册(免费)阅读本文的第一部分( 链接 ),但如果你是C ++和数学,你应该这样做…

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

作为示例,请考虑以下awk,nawk,gawk代码,它们在bash shell脚本中用于创建新的(随机)mac地址 – 即代码片段中引用的.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脚本中的代码片段是:

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

如果我没有弄错,srand的种子值是从系统时钟得到的。

我希望这有助于您了解可行的编码解决方案。

如前所述,它是一个线性同余,(如果你想深入评论这些如何生成伪随机值,你可以查看它)

种子存储在_getptd() – > _ holdrand(以下称为holdrand)中

这段代码通过正常的乘法和加法步骤“工作”,然后溢出holdrand以获得隐含的“模数”0x100000000。

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

从技术上说,溢出一个整数变量是一种未定义的行为,但在大多数平台上,它是可以预测的,所以微软的工程师忽略了这个问题。