[英]Understanding the algorithm of Visual C++'s rand() function
在 C/C++ 中,我们通常会使用rand()
和srand()
来获取随机 integer。 但是当我尝试自己重写时,发现算法很难理解。 function 很容易只用几行写出来,但公式是误解。
主要公式:
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)发表于 Dr. Dobb's Journal (DDJ) 的一篇优秀文章中找到对线性同余生成器 (LCG) 和其他类似系列或伪随机生成器的解释,以及关于这些特定常数的选择的说明:快速、高质量、并行随机数生成器:比较实现。
我认为您需要在 DDJ 的网站上注册(免费)才能阅读本文的第一部分(链接),但是如果您喜欢 C++ 和数学,无论如何您都应该这样做...
正如已经说过的那样,它是一个线性同余,(如果您想深入了解这些如何生成伪随机值,您可以查看它)
种子存储在_getptd()->_holdrand(以下称为holdrand)
此代码通过执行正常的乘法和加法步骤“起作用”,但随后溢出 holdrand 以获得隐含的“模数”0x100000000。
我提到这一点是因为它不是很明显,而且通常不被认为是好的风格。
从技术上讲,溢出 integer 变量会引发未定义的行为,但在大多数平台上它是完全可以预测的,因此微软的工程师忽略了这个问题。
在调用 rand 之前,由于 srand 的手册页指出“如果没有提供种子值,则 rand() function 会自动为种子值 1 提供种子”,因此调用 rand 的更好方法是首先调用 srand,这将“将其参数设置为 rand() 返回的新伪随机整数序列的种子”。
例如,考虑在 bash shell 脚本中使用的 awk、nawk、gawk 代码来创建新的(参考)macaddrrandom 脚本。
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 的种子值来自系统时钟。
我希望这可以帮助您了解一种可行的编码解决方案的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.