导读
Redis源码中有一个rand.c的源文件,很明显这是一个和(伪)随机数有关的文件。细看该文件代码只有寥寥50行,不过涉及到的算法原理却不简单,读起来虽然有些晦涩,但对于深入理解48位空间中的伪随机数算法是不可多得的范本。作者在该文件的注释中写道:这个伪随机数生成函数是从pysam源码中的drand48()派生过来的。关于pysam是什么项目,并不是重点,其实很多Unix系统中都存在drand48这个函数(SVr4,POSIX.1-2001),我们可在终端中man一下drand48。
可以看到在头文件stdlib.h中定义了一系列和随机数有关的函数,除drand48()外,还有erand48()、lrand48()、nrand48()等等。所谓的48指的是随机数种子的二进制位数。它们的主要操作使用的都是同一个随机数生成算法,但是后续的处理不同,比如drand48()、erand48()返回的是 [0.0, 1.0)之间的浮点型,而lrand48()和nrand48()返回的是[0, 2^31)之间的整数。rand.c文件中有一个重要的函数——redisLrand48()。虽然作者是受了drand48()源码的启发编写的该函数,但实际上redisLrand48()和lrand48()更像,拥有几乎一样的编程接口,并且设置相同的随机数种子,生成的伪随机数序列相同。
继续读rand.c开头的注释,可以发现作者编写这个文件的目的是用于替换Lua中实现的math.random()函数。作者认为Lua随机数生成算法的默认实现中使用了libc的rand()函数,而libc的rand()并不能保证在设置了相同种子的情况下,不同平台能生成相同的随机数序列。因此作者重写这一文件,保证跨平台性。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
算法原理
线性同余方程
Xn+1 = (aXn + c) mod m, where n >= 0
//默认情况下
a = 0x5DEECE66D
c = 0xB
m = 2^48