from:https://www.xcode.me/more/net-csharp-generate-random
随机数生成要领可以说是任何编程语言必备的成果,它的重要性不言而言,在C#中我们凡是使用Random类生成随机数,在一些场景下,我却发明Random生成的随机数并不成靠,不才面的例子中我们通过循环随机生成5个随机数:
for (int i = 0; i < 5; i++) { Random random = new Random(); Console.WriteLine(random.Next()); }这段代码执行后的功效如下所示:
2140400647 2140400647 2140400647 2140400647 2140400647通过以上功效可知,随机数类生成了5个不异的数,这并非我们的预期,为什么呢?为了弄清楚这个问题,零度分解了微软官方的开源Random类,发此刻C#中生成随机数使用的算法是线性同余法,经百科而知,这种算法生成的不是绝对随机,而是一种伪随机数,线性同余法算法的的公式是:
第N+1个数 = ( 第N个数 * A + B) % M上面的公式中A、B和M分袂为常数,是生成随机数的因子,如果之前从未通过同一个Random东西生成过随机数(也就是挪用过Next要领),那么第N个随机数为将被指定为一个默认的常数,这个常数在创建一个Random类时被默认值指定,Random也供给一个结构函数允许开发者使用本身的随机数因子,这一切可通过微软官方开源代码看到:
public Random() : this(Environment.TickCount) { } public Random(int Seed) { }通过默认结构函数创建Random类时,一个Environment.TickCount东西作为因子被默认通报给第二个结构函数,Environment.TickCount暗示操纵系统启动后颠末的毫秒数,计算机的运算运算速度远比毫秒要快得多,这导致一个的具有毫秒精度的因子参预随机数的生成过程,但在5次循环中,我们使用了同一个毫秒级的因子,从而生成不异的随机数,此外,第N+1个数的生成与第N个数有着直接的关系。
在上面的例子中,,假设系统启动以来的毫秒数为888毫秒,执行5次循环用时只有0.1毫秒,这导致在循环中创建的5个Random东西都使用了不异的888因子,每次被创建的随机东西又使用了不异的第N个数(默认为常数),通过这样的假设我们不难看出,上面的功效是一定的。
此刻我们转变这个款式,在循环之外创建一个Random东西,在每次循环中引用它,并通过它生成随机数,并在同一个东西上多次挪用Next要领,从而不停变革第N个数,代码如下所示:
Random random = new Random(); for (int i = 0; i < 5; i++) { Console.WriteLine(random.Next()); }执行后的功效如下所示:
391098894 1791722821 1488616582 1970032058 201874423我们看到这个功效确实证实了我们上面的揣度,第1次循环时公式中的第N个数为默认常数;当第二次循环时,第N个数为391098894,随后不停变革的第N个数作为因子参估量算,这保证了功效的随机性。
虽然通过我们的随机数看起来也很随机了,但肯定这个算法是伪随机数,当第N个数和因子都不异时,生成的随机数仍然是反复的随机数,由于Random供给一个带参的结构函数允许我们传入一个因子,如果传入的因子随机性强的话,那么生成的随机数也会对照可靠,为了供给一个可靠点的因子,我们凡是使用GUID孕育产生填充因子,同样放在循环中测试:
for (int i = 0; i < 5; i++) { byte[] buffer = Guid.NewGuid().ToByteArray(); int iSeed = BitConverter.ToInt32(buffer, 0); Random random = new Random(iSeed); Console.WriteLine(random.Next()); }这样的方法保证了填充因子的随机性,所以生成的随机数也对照可靠,运行功效如下所示:
734397360 1712793171 1984332878 819811856 1015979983在一些场景下这样的随机数并不成靠,为了生成越发可靠的随机数,微软在System.Security.Cryptography定名空间下供给一个名为RNGCryptoServiceProvider的类,它给与系统当前的硬件信息、进程信息、线程信息、系统启动时间和当前精确时间作为填充因子,通过更好的算法生成高质量的随机数,它的使用要领如下所示:
byte[] randomBytes = new byte[4]; RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider(); rngServiceProvider.GetBytes(randomBytes); Int32 result = BitConverter.ToInt32(randomBytes, 0);通过这种算法生成的随机数,颠末成千上万次的测试,并未发明反复,质量简直比Random高了很多。此外windows api也供给了一个非托管的随机数生成函数CryptGenRandom,CryptGenRandom与RNGCryptoServiceProvider的道理类似,给与C++编写,如果要在.NET中使用,需要进行简单的封装。它的原型如下所示:
BOOL WINAPI CryptGenRandom( _In_ HCRYPTPROV hProv, _In_ DWORD dwLen, _Inout_ BYTE *pbBuffer );