梅森旋转算法
茶蒎o
编辑于 2023年12月02日 00:45

梅森旋转算法(Mersenne Twister)是一种用于产生伪随机数序列的算法。它由松本真(Makoto Matsumoto)和西村拓士(Takuji Nishimura)于1997年开发,是一种广泛使用的伪随机数生成器。该算法以梅森素数(Mersenne Prime)命名,是一种 32 位或 64 位的伪随机数生成器。

以下是梅森旋转算法的一些关键特点和原理:

  1. 周期长:梅森旋转算法的主要优点之一是其非常长的周期。对于 32 位版本,它的周期为 2^19937 - 1,而对于 64 位版本,周期更长。

  2. 均匀分布:梅森旋转算法在统计学上被认为是均匀分布的,并且在许多应用中表现良好。这是由于算法使用了梅森素数,确保了生成的序列具有良好的统计性质。

  3. 初始化种子:算法需要一个初始的种子来启动。如果使用相同的初始种子,生成的随机数序列将完全相同,这在某些情况下可能是有用的。

  4. 梅森素数:算法中使用的梅森素数是形如 2^p - 1 的素数。其中,p 是素数,而 2^p - 1 也是素数。这样的素数能够提供较长的周期。

  5. 旋转运算:算法中使用了旋转运算(shift-xor)来生成随机数。这有助于保持较长周期和均匀分布的特性。

梅森旋转算法已经成为许多编程语言的标准库中默认的伪随机数生成器,包括 Python、Java、C# 和 C++ 等。然而,在某些安全性要求较高的应用中,可能需要更为复杂的密码学安全的伪随机数生成器。

栗子:

/// <summary>

    /// 梅森旋转算法

    /// </summary>

    public class MersenneTwister

{

        //uint无符号整数

        private const int N = 624;//表示生成随机数序列时使用的数组大小。

        private const int M = 397;//用于确定生成随机数时使用的元素之间的关系。

        private const uint MatrixA = 0x9908b0df;//用于进行异或运算,引入更多的随机性。

        private const uint UpperMask = 0x80000000;//是一个掩码,用于提取32位无符号整数的最高有效位。

        private const uint LowerMask = 0x7fffffff;//是一个掩码,用于提取32位无符号整数的低31位。

        private uint[] mt = new uint[N];

        private int index;

        public MersenneTwister(uint seed)

        {

            Initialize(seed);

        }

        private void Initialize(uint seed)//传入初始值

        {

            mt[0] = seed;

            for (int i = 1; i < N; i++)

            {

                mt[i] = (uint)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);

                //32位:31个数字右移30位,还剩一位   只是对最后一位进行改变

                // mt[i - 1] ^ (mt[i - 1] >> 30):右移30位,然后与原来的 mt[i - 1] 进行异或操作。这一步是为了从 mt[i - 1] 中提取一部分高位信息,引入更多的低位随机性。

                //加上索引 i 是为了在不同的状态元素中引入不同的偏移,防止在初始状态时所有的状态元素都相等。

            }

        }

        public uint Next()

        {

            if (index == 0)//第一次进来是走这里

            {

                GenerateNumbers();

            }

            uint y = mt[index];

            y ^= y >> 11;               //通过右移11位,将 y 的高11位移动到低11位,然后与原来的 y 进行异或,引入了更多的低位信息,增加了混乱性。

            y ^= (y << 7) & 0x9d2c5680; //通过左移7位,将 y 的低7位移动到高7位,然后与掩码 0x9d2c5680 进行与运算,保留一部分位的信息,最后与原来的 y 进行异或,引入了更多的高位信息,增加了混乱性。

            y ^= (y << 15) & 0xefc60000;//通过左移15位,将 y 的低15位移动到高15位,然后与掩码 0xefc60000 进行与运算,保留一部分位的信息,最后与原来的 y 进行异或,引入了更多的高位信息,增加了混乱性。

            y ^= y >> 18;               //通过右移18位,将 y 的高18位移动到低18位,然后与原来的 y 进行异或,引入了更多的低位信息,增加了混乱性。

            index = (index + 1) % N;//index处于数组大小的范围内

            return y;

        }

        private void GenerateNumbers()

        {

            for (int i = 0; i < N; i++)

            {

                //mt[i] & UpperMask:提取 mt[i] 的最高位。

                //mt[(i + 1) % N] & LowerMask:提取 mt[(i + 1) % N] 的低31位。

                uint y = (mt[i] & UpperMask) | (mt[(i + 1) % N] & LowerMask);//将 mt[i] 的最高位与 mt[(i + 1) % N] 的低31位组合在一起,形成中间值 y。

                mt[i] = mt[(i + M) % N] ^ (y >> 1);                          //将之前获取的下一个状态元素与右移得到的 y 结合在一起,引入更多的混淆性和随机性。

                if (y % 2 != 0)

                {

                    mt[i] ^= MatrixA;//引入更多的混淆和变化,以增加梅森旋转算法生成随机数的复杂性。

                }

            }

        }

    }