伪随机数生成——梅森旋转(Mersenne Twister/MT)算法笔记

前言

最近在看吴军博士的《数学之美》一书,把很多之前没注意到,没用到,甚至不知道怎么用的数学知识和实际问题联系了起来,感觉打开了新世界的大门一样。这本书很多知识点还有技术都是点到为止,并没有深入,所谓师傅领进门,修行在个人吧。所以从本篇开始,博主将对数学之美一书中的一些提到的东西做个总结。不对的地方希望各位看官大佬们多多指正。

在本书第16章《信息指纹及其应用》一文中,介绍到了现在常用的一个伪随机数生成算法,梅森旋转(Mersenne Twister/MT)算法,它的周期长,对于一个k位2进制数,梅森旋转算法可在[0,2^k-1]的范围内生成离散型均匀分布的随机数。

以下内容大多翻译自Wikipedia(中文wiki里的和英文的差的实在太多了……不然我就直接搬运了)

正文

优点

·许可免费,而且对所有它的变体专利免费(除CryptMT外)
·几乎无处不在:它被包含在大多数编程语言和库中
·通过了包括Diehard测试在内的大多数统计随机性测试(除TestU01测试外)
·在应用最广泛的MT19937变体中,周期长达2^19937-1
·在MT19937-32的情况下对1 ≤ k ≤ 623,满足k-分布
·比其他大多数随机数发生算法要快

k-分布

一个周期为Pw位整数的随机序列xi,当满足如下条件时被称为满足v位的k-分布:

假设truncv(x)表示x的前v位形成的数字,并且长度为P的kv位序列:
序列图示
其中每个可能出现的2^kv组合在一个周期内出现相同的次数(除全0序列出现次数次数比其他序列少1次)

缺点

·需要大量的缓冲器(2.5kib),但在TinyMT版本中修正
·吞吐量中等,但在SFMT版本中修正
·产生的随机数与seed相关,不能用于蒙特卡洛模拟
·由相同的初始序列产生的随机状态几乎相同,但在2002年的更新中对MT算法的初始化进行了改进,使得对于相同的初始序列也能产生不同的随机状态
·非加密安全的,除CryptMT外

算法详细

本算法基于标准(线性)旋转反馈移位寄存器(twisted generalised feedback shift register/TGFSR)产生随机数

算法中用到的变量如下所示:
    ·w:长度(以bit为单位)
    ·n:递归长度
    ·m:周期参数,用作第三阶段的偏移量
    ·r:低位掩码/低位要提取的位数
    ·a:旋转矩阵的参数
    ·f:初始化梅森旋转链所需参数
    ·b,c:TGFSR的掩码
    ·s,t:TGFSR的位移量
    ·u,d,l:额外梅森旋转所需的掩码和位移量


MT19937-32的参数列表如下:
    ·(w, n, m, r) = (32, 624, 397, 31)
    ·a = 9908B0DF(16)
    ·f = 1812433253
    ·(u, d) = (11, FFFFFFFF16)
    ·(s, b) = (7, 9D2C568016)
    ·(t, c) = (15, EFC6000016)
    ·l = 18

MT19937-64的参数列表如下:
    ·(w, n, m, r) = (64, 312, 156, 31)
    ·a = B5026F5AA96619E9(16)
    ·f = 6364136223846793005
    ·(u, d) = (29, 555555555555555516)
    ·(s, b) = (17, 71D67FFFEDA6000016)
    ·(t, c) = (37, FFF7EEE00000000016)
    ·l = 43

梅森旋转算法图示

整个算法分为三个阶段(如图所示):
第一阶段:初始化,获得基础的梅森旋转链;
第二阶段:对于旋转链进行旋转算法;
第三阶段:对于旋转算法所得的结果进行处理;

初始化

首先将传入的seed赋给MT[0]作为初值,然后根据递推式:MT[i] = f × (MT[i-1] ⊕ (MT[i-1] >> (w-2))) + i递推求出梅森旋转链。伪代码如下:

 // 由一个seed初始化随机数产生器
 function seed_mt(int seed) {
     index := n
     MT[0] := seed
     for i from 1 to (n - 1) {
         MT[i] := lowest w bits of (f * (MT[i-1] xor (MT[i-1] >> (w-2))) + i)
     }
 }

对旋转链执行旋转算法

遍历旋转链,对每个MT[i],根据递推式:MT[i] = MT[i+m]⊕((upper_mask(MT[i]) || lower_mask(MT[i+1]))A)进行旋转链处理。

其中,“||”代表连接的意思,即组合MT[i]的高 w-r 位和MT[i+1]的低 r 位,设组合后的数字为x,则xA的运算规则为(x0是最低位):
xA运算规则

伪代码为:

lower_mask = (1 << r) - 1
upper_mask = !lower_mask
 // 旋转算法处理旋转链 
 function twist() {
     for i from 0 to (n-1) {
         int x := (MT[i] & upper_mask)+ (MT[(i+1) mod n] & lower_mask)
         int xA := x >> 1
         if (x mod 2) != 0 { 
         // 最低位是1
             xA := xA xor a
         }
         MT[i] := MT[(i + m) mod n] xor xA
     }
     index := 0
}

对旋转算法所得结果进行处理

设x是当前序列的下一个值,y是一个临时中间变量,z是算法的返回值。则处理过程如下:
y := x ⊕ ((x >> u) & d)
y := y ⊕ ((y << s) & b)
y := y ⊕ ((y << t) & c)
z := y ⊕ (y >> l)
伪代码如下:

// 从MT[index]中提取出一个经过处理的值
// 每输出n个数字要执行一次旋转算法,以保证随机性
 function extract_number() {
     if index >= n {
         if index > n {
           error "发生器尚未初始化"
         }
         twist()
     }

     int x := MT[index]
     y := x xor ((x >> u) and d)
     y := y xor ((y << s) and b)
     y := y xor ((y << t) and c)
     z := y xor (y >> l)

     index := index + 1
     return lowest w bits of (z)
 }
MT-19937-32实现代码(C语言版)
#include <stdint.h>

// 定义MT19937-32的常数
enum
{
    // 假定 W = 32 (此项省略)
    N = 624,
    M = 397,
    R = 31,
    A = 0x9908B0DF,

    F = 1812433253,

    U = 11,
    // 假定 D = 0xFFFFFFFF (此项省略)

    S = 7,
    B = 0x9D2C5680,

    T = 15,
    C = 0xEFC60000,

    L = 18,

    MASK_LOWER = (1ull << R) - 1,
    MASK_UPPER = (1ull << R)
};

static uint32_t  mt[N];
static uint16_t  index;

// 根据给定的seed初始化旋转链
void Initialize(const uint32_t  seed)
{
    uint32_t  i;
    mt[0] = seed;
    for ( i = 1; i < N; i++ )
    {
        mt[i] = (F * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
    }
    index = N;
}

static void Twist()
{
    uint32_t  i, x, xA;
    for ( i = 0; i < N; i++ )
    {
        x = (mt[i] & MASK_UPPER) + (mt[(i + 1) % N] & MASK_LOWER);
        xA = x >> 1;
        if ( x & 0x1 )
        {
            xA ^= A;
        }
        mt[i] = mt[(i + M) % N] ^ xA;
    }

    index = 0;
}

// 产生一个32位随机数
uint32_t ExtractU32()
{
    uint32_t  y;
    int       i = index;
    if ( index >= N )
    {
        Twist();
        i = index;
    }
    y = mt[i];
    index = i + 1;
    y ^= (y >> U);
    y ^= (y << S) & B;
    y ^= (y << T) & C;
    y ^= (y >> L);
    return y;
}
### 回答1: 在MATLAB中,我们可以使用内置函数fft来进行快速傅里叶变换。FFT需要计算一些旋转因子,这些因子通常是预先计算好的。这些旋转因子称为Twiddle Factors,通常使用一维数组表示。Twiddle Factors包含以下公式: W(n,k) = e^(−2πikn/N) 其中,n是序列的下标,k是频率的下标,N是序列大小。在MATLAB中,可以使用如下代码计算出Twiddle Factors: N = length(x); w = exp(-2*pi*1i/N); for k = 1:N/2 wk(k) = w^(k-1); end w = [wk wk]; 该代码将计算一个大小为Nx1的向量w,其中包含Twiddle Factors。中间两行的代码​计算了复数单位根,然后使用循环语句k从1迭代到N/2,wk(k)为N/2个序列的旋转因子。最后,使用[wk wk]代表包含全部的Twiddle Factors。使用得到的Twiddle Factors进行FFT计算。 ### 回答2: MATLAB是科学计算和数据分析领域中,应用最广泛的工具之一。在信号处理领域,经常需要用到快速傅里叶变换(FFT)。而FFT算法中的旋转因子是非常重要的一部分。下面,我们来详细了解一下MATLAB中如何计算FFT的旋转因子。 首先,我们需要了解FFT算法中的旋转因子。它实际上是一个复数,记为$\omega_n^{k}$,其中$n$表示FFT长度,$k$表示当前迭代的次数,其计算公式如下: $$ \omega_n^{k} = \cos\frac{2\pi k}{n} - j\sin\frac{2\pi k}{n} $$ 其中$j$为虚数单位。该公式中,旋转因子是由角度为$2\pi k/n$的单位圆上的点得到的。 在MATLAB中,计算FFT旋转因子的方法非常简单。可以通过内置函数fft()来实现。该函数会返回一个与输入信号大小相同的向量,向量的每一个元素都是一个旋转因子。我们只需要按照公式计算即可。 例如,假设我们需要计算FFT长度为8的旋转因子,可以使用以下代码: ```matlab N = 8; omega = fft(eye(N)); ``` 其中,fft(eye(N))表示生成一个大小为N×N的单位矩阵,然后进行FFT计算。 计算结果将返回一个8×8的向量,向量的每一个元素都是一个旋转因子。我们可以使用plot()函数将它们绘制出来,从而看到它们在单位圆上的分布情况。代码如下: ```matlab figure; hold on; plot(omega, &#39;b.-&#39;); plot(real(omega), imag(omega), &#39;ro&#39;); axis equal; legend(&#39;复数形式&#39;, &#39;实部-虚部&#39;); title(&#39;FFT旋转因子&#39;); hold off; ``` 该代码中,我们先生成一个空白的图像,并使用hold on命令来保留该图像。然后,分别绘制旋转因子的实部和虚部,以及复数形式下的分布情况。最后,使用axis equal命令来设置坐标轴的比例相等,以便更好地展示单位圆上的情况。 以上就是MATLAB计算FFT旋转因子的方法。通过这个方法,我们可以方便地计算出FFT算法中所需的旋转因子,进而实现快速而准确的信号处理和分析。 ### 回答3: Fourier变换在信号处理、图像处理领域有着很广泛的应用。而fft算法是快速计算DFT(离散傅里叶变换)的一种算法。在实际应用中,我们需要计算DFT的旋转因子,Matlab提供了几种函数实现这个功能。 Matlab中计算fft旋转因子的函数主要有以下几种: 1. fft – 默认情况下,该函数内置的旋转因子是以复数形式存储的,可以通过修改函数输入参数来控制旋转因子的类型。 2. fft2 – 该函数是计算2维FFT的,旋转因子的计算方式与fft基本一致。 3. fftn – 该函数是计算n维FFT的,其中n可以是2、3等。该函数的旋转因子计算方式基本与fft一致。 4. ifftshift – 该函数可以用来调整输入信号的零频分量位置,通常会在fft操作前使用,以保证计算结果的正确性。 通过以上几种函数,我们可以很方便地计算FFT的旋转因子,在实际应用中也经常用到它们。其中,fft的应用最为广泛,因为这种算法不仅计算快,而且精度较高。此外,在进行FFT计算时,我们还应该注意采样间隔、采样点数、频率分辨率等参数的设置,以保证计算结果的有效性和准确性。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值