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

时间:2011-07-22 16:27:17

标签: c++ c algorithm math

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

5 个答案:

答案 0 :(得分:18)

这只是模数运算。你是乘以并加上一个以2 ^ 32为模的数字(例如),并将高16位作为“随机”数返回。因为你正在增加和增加与模数相互作用的数字,这会产生一些均匀分布的数字。

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

此方案称为linear congruential

答案 1 :(得分:4)

伪随机数生成器是Linear Congruential Generator

答案 2 :(得分:2)

你可以在本月(7-2011)博士Dobb杂志上发表的一篇优秀文章中找到线性同余发生器(LCG)和其他类似系列或伪随机发生器的解释,以及这些特定常数的选择。 (DDJ):Fast, High-Quality, Parallel Random-Number Generators: Comparing Implementations

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

答案 3 :(得分:0)

在调用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的种子值是从系统时钟得到的。

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

答案 4 :(得分:0)

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

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

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

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

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