在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 );
}
答案 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。
我之所以提到这一点,是因为它并不是很明显,而且通常不被认为是好的风格。
从技术上说,溢出整数变量是一种未定义的行为,但在大多数平台上它是可以预测的,所以微软的工程师忽略了这个问题。