算法定义
伪随机数
伪随机数就是可以通过特定的初始值可以得出特定的伪随机数,这些伪随机数在统计上具有均匀性、独立性。所以在现实生活中,我们可以通过伪随机数生成算法来满足生成随机数的需求。
梅森素数旋转法
它是一种伪随机数生成算法,它能够根据某个初始种子生成随机串。由于该该算法的"旋转"特性,生成的随机串是有周期的。在周期外的数是可以通过上一周期产生的随机串推出来,故在周期外就不具备随机中的独立性了。但是在此算法中,我们不必担心,因为它的周期是梅森素数2 19937 − 1 2^{19937}-1219937−1,这数非常大,大到足以让我们安心的使用此随机数生成算法。比如像c++、R、python、MATLAB的随机数产生都是运用此算法实现。比较常见的有两种算法,分别是基于32位的MT19937-32和基于64位的MT19937-64,在此我们介绍的是MT19937-32,MT19937-32算法能够生成[ 0 , 2 32 − 1 ] [0,2^{32}-1][0,232−1]范围内的整数随机数.
k-distributed to v-bit accuracy(k分布v位准确度)
生成随机数算法有很多,所以需要一个标准来评估这些随机数生成算法的质量。其中一个度量算法就是k-distributed to v-bit accuracy(翻译不准确,故在此不翻译啦~)。接下来具体来讲下这个评估算法:
假设某一个随机数生成算法能够产生周期为P PP的w ww比特的随机串{x i x_ixi},同时将w ww比特的随机数x xx的最高v vv(前v vv)位组成的数记作为t r u n c v ( x ) trunc_v(x)truncv(x).因此可以构造出如下的二进制数,长度为k v kvkv比特:
P R N G k , v ( i ) = ( t r u n c v ( x i ) , t r u n c v ( x i + 1 ) , . . . , t r u n c v ( x i + k − 1 ) ) PRNG_{k,v}(i) = (trunc_v(x_i),trunc_v(x_{i+1}),...,trunc_v(x_{i+k-1}))PRNGk,v(i)=(truncv(xi),truncv(xi+1),...,truncv(xi+k−1))
显而易见,P R N G k , v ( i ) PRNG_{k,v}(i)PRNGk,v(i)的长度是kv,所以总共有2 k v 2^{kv}2kv种取值。如果我们说随机串{x i x_ixi}是满足k-distributed to v-bit accuracy的,则当x i x_ixi取遍周期P内的值时,P R N G k , v ( i ) PRNG_{k,v}(i)PRNGk,v(i)在2 k v 2^{kv}2kv种取值中是均匀的。
对于固定的v,我们易知,如果{x i x_ixi}满足k-distributed to v-bit accuracy,那么他一定是满足 (k-1)-distributed to v-bit accuracy.在原论文中我们证明了MT19937-32算法生成的随机串{x i x_ixi}是满足 623-distributed to 32-bit accuracy的。所以生成的随机数序列{x i x_ixi}一定满足1-distributed to 32-bit accuracy.所以可证明随机数序列{x i x_ixi}在周期长度为P(2 19937 − 1 2^{19937}-1219937−1)内是均匀分布的。
算法步骤
旋转
梅森旋转素数法总共分两步,第一步为旋转,第二步为提取。此节介绍下旋转过程,旋转过程主要有如下公式实现:
x n + k : = x m + k ⊕ ( x k u ∣ x k + 1 l ) ∗ A x_{n+k}:=x_{m+k}\oplus(x_{k}^u|x_{k+1}^l)*Axn+k:=xm+k⊕(xku∣xk+1l)∗A
符号解释:
- x i x_ixi:随机数序列{x i x_ixi}中的第i ii个元素,在此计算中x i x_ixi可看成是1 ∗ w 1*w1∗w的行向量,即可写成向量x = ( x w − 1 , x w − 2 , . . . , x 0 ) x=(x_{w-1},x_{w-2},...,x_0)x=(xw−1,xw−2,...,x0).x i x_ixi实际可以看成我们生成的随机整数的二进制形式。
- n : n:n:随机数列{x i x_ixi}的长度,在不同形式的随机算法,n nn取值各不相同,在MT19937-32中,我们取n nn为623.
- m : m ∈ [ 1 , n ] . m:m\in[1,n].m:m∈[1,n].在不同形式的随机算法,m mm取值各不相同,在MT19937-32中,我们取m mm为397.
- x u : x^u:xu:指的是由x xx前u uu位组成1 ∗ w 1*w1∗w的行向量,向右补零。
- x l : x^l:xl:指的是由x xx后l ll位组成1 ∗ w 1*w1∗w的行向量,向左补零。
- 矩阵A w ∗ w A_{w*w}Aw∗w的形式如下:
A = [ 0 1 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ 1 a w − 1 a w − 2 ⋯ a 0 ] A= \left[ \begin{matrix} 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \\ a_{w-1} & a_{w-2} & \cdots & a_0 \\ \end{matrix} \right]A=⎣⎢⎢⎢⎡0⋮0aw−11⋮0aw−2⋯⋱⋯⋯0⋮1a0⎦⎥⎥⎥⎤
所以我们可知:
x A = { x > > 1 x 0 = 0 x > > 1 ⊕ a ^ x 0 = 1 xA=\left\{ \begin{aligned} x>>1 & &x_0=0\\ x>>1\oplus \hat a & &x_0=1 \end{aligned} \right.xA={x>>1x>>1⊕a^x0=0x0=1
在梅森旋转算法中,它是利用线性反馈移位寄存器来实现旋转的。所以我们先来介绍线性反馈移位寄存器。反馈移位寄存器主要有两部分组成:
- 级,参与变换的长度。
- 反馈函数,如果反馈函数为线性则称为线性反馈移位寄存器,若为非线性,则称为非线性反馈移位寄存器。
一般来说,LFSR是基于异或操作的,具体步骤如下:
- 将原寄存器中的最低位作为输出。
- 按照反馈函数,选择若干高位依次取抑或,然后与步骤一中的输出(最低位)取抑或作为输出。
- 将原寄存器中的元素向右移一位,然后将步骤二中的输出作为最高位。最终寄存器中的数即为此次旋转后得到的数。
举个例子,假设反馈函数是f ( x ) = x 4 + x 2 + x + 1 f(x)=x^4+x^2+x+1f(x)=x4+x2+x+1,即需要将最高位与第三高位取抑或,然后再与最低位取抑或得到的数作为移位后的最高位,假设初始值位1000,则变换如下:
- 1000
- 1100
- 1110
- 0111
- 0011
- 0001
- 1000
此变换周期为6,当然不同的反馈函数周期是不一样的,比如反馈函数f ( x ) = x 4 + x + 1 f(x)=x^4+x+1f(x)=x4+x+1,它的周期达到了15,它能够取遍4位的所有值(0除外,0不管怎么变换都是0),大家有兴趣可以去验证下。在此,MT19937-32在旋转中,周期可高达2 19937 − 1 2^{19937}-1219937−1
提取
MT19937具备两个特性,一个是周期大,另一个是满足 623-distributed to 32-bit accuracy.上面的寄存器达到了我们周期大的目的。接下来的提取,则可以满足第二特性。
提取的方法就是在每次旋转后再乘以个k可逆矩阵T即可,步骤如下:
- y ← x ⊕ ( x > > u ) y \leftarrow x \oplus (x>>u)y←x⊕(x>>u)
- y ← y ⊕ ( ( y < < s ) y \leftarrow y \oplus ((y<<s)y←y⊕((y<<s) AND b ) b)b)
- y ← y ⊕ ( ( y < < t ) y \leftarrow y \oplus ((y<<t)y←y⊕((y<<t) AND c ) c)c)
- z ← y ⊕ ( y > > l ) z \leftarrow y \oplus (y>>l)z←y⊕(y>>l)
算法步骤
由此可见,梅森旋转法主要分为两步:旋转和提取。具体算法步骤如下:
参数说明:
- w:生成随机数的二进制长度
- n:参与旋转的随机数个数(旋转的深度)
- m:参与旋转的中间项
- r : x k ( u ) x_k^{(u)}xk(u)与x k + 1 ( l ) x_{k+1}^{(l)}xk+1(l)的切分位
- a: 矩阵A的最后
- u,s,t,l:整数参数,移位运算的移动距离;
- b,c :比特遮罩。
于是算法步骤如下:
1.初始化
- l o w e r ← 0 lower\leftarrow0lower←0
- l o w e r ← ( l o w e r < < 1 ) lower\leftarrow(lower<<1)lower←(lower<<1) AND 1 for r times
- u p p e r ← upper\leftarrowupper← COMPL l o w e r lowerlower
- a ← a w − 1 a w − 2 . . . a 1 a 0 a\leftarrow a_{w-1}a_{w-2}...a_1a_0a←aw−1aw−2...a1a0
- i ← 0 i\leftarrow 0i←0
2.非零初始化
x [ 0 ] , x [ 1 ] , . . . , x [ n − 1 ] x[0],x[1],...,x[n-1]x[0],x[1],...,x[n−1]
3.旋转
- t ← ( x [ i ] t\leftarrow (x[i]t←(x[i] A N D ANDAND u p p e r ) upper)upper) OR ( x [ ( i + 1 ] ) (x[(i+1])(x[(i+1]) mod n ] n]n] AND l o w e r ) lower)lower)
- x [ i ] ← x [ ( i + m ) x[i]\leftarrow x[(i+m)x[i]←x[(i+m) MOD n ] n]n] XOR ( t > > 1 ) (t>>1)(t>>1) XOR { 0 , if t 0 = 0 a , otherwise \begin{cases} 0 , & \text{if }t_0=0 \\ a , & \text{otherwise} \end{cases}{0,a,if t0=0otherwise
4.提取输出
- y ← x [ i ] y \leftarrow x[i]y←x[i]
- y ← y X O R ( y > > u ) y \leftarrow y XOR(y>>u)y←yXOR(y>>u)
- y ← y X O R ( ( y < < s ) y \leftarrow y XOR ((y<<s)y←yXOR((y<<s) AND b ) b)b)
- y ← y X O R ( ( y < < t ) y \leftarrow y XOR ((y<<t)y←yXOR((y<<t) AND c ) c)c)
- z ← y X O R ( y > > l ) z \leftarrow y XOR (y>>l)z←yXOR(y>>l)
5.更新循环变量i ← ( i + 1 ) i\leftarrow(i+1)i←(i+1) MOD n nn,而后跳转至步骤3 继续进行。
文献:
- https://liam0205.me/2018/01/12/Mersenne-twister/
- https://blog.csdn.net/Touch_Dream/article/details/68948708