随机数的定义为:产生的所有数字毫无关系.
在实际应用中很多地方会用到随机数,比如需要生成唯一的订单号.
在C#中获取随机数有三种方法:
一.Random 类
Random类默认的无参构造函数可以根据当前系统时钟为种子,进行一系列算法得出要求范围内的伪随机数.
这种随机数可以达到一些要求较低的目标,但是如果在高并发的情况下,Random类所取到的系统时钟种子接近甚至完全一样,就很有可能出现重复,这里用循环来举例
这个例子会输出10个相同的"随机数".
突显出的问题:因为Random进行伪随机数的算法是固定的,所以根据同一个种子计算出的数字必然是一样的.而以当代计算机的运行速度,该循环几乎是在瞬间完成的,种子一致,所以会出现10次循环输出同一随机数的情况.
有的时候使用random生成随机数的时候往往不是随机的 这是为什么呢?
随机数生成方法可以说是任何编程语言必备的功能,它的重要性不言而言,在C#中我们通常使用Random类生成随机数,在一些场景下,我却发现Random生成的随机数并不可靠,在下面的例子中我们通过循环随机生成5个随机数:
for (int i &#61; 0; i <5; i&#43;&#43;) { Random random &#61; new Random(); Console.WriteLine(random.Next()); }
这段代码执行后的结果如下所示&#xff1a;
2140400647 2140400647 2140400647 2140400647 2140400647
通过以上结果可知&#xff0c;随机数类生成了5个相同的数&#xff0c;这并非我们的预期&#xff0c;为什么呢&#xff1f;为了弄清楚这个问题&#xff0c;零度剖析了微软官方的开源Random类&#xff0c;发现在C#中生成随机数使用的算法是线性同余法&#xff0c;经百科而知&#xff0c;这种算法生成的不是绝对随机&#xff0c;而是一种伪随机数&#xff0c;线性同余法算法的的公式是&#xff1a;
第N&#43;1个数 &#61; ( 第N个数 * A &#43; B) % M
上面的公式中A、B和M分别为常数&#xff0c;是生成随机数的因子&#xff0c;如果之前从未通过同一个Random对象生成过随机数(也就是调用过Next方法)&#xff0c;那么第N个随机数为将被指定为一个默认的常数&#xff0c;这个常数在创建一个Random类时被默认值指定&#xff0c;Random也提供一个构造函数允许开发者使用自己的随机数因子&#xff0c;这一切可通过微软官方开源代码看到&#xff1a;
public Random() : this(Environment.TickCount) { } public Random(int Seed) { }
通过默认构造函数创建Random类时&#xff0c;一个Environment.TickCount对象作为因子被默认传递给第二个构造函数&#xff0c;Environment.TickCount表示操作系统启动后经过的毫秒数&#xff0c;计算机的运算运算速度远比毫秒要快得多&#xff0c;这导致一个的具有毫秒精度的因子参与随机数的生成过程&#xff0c;但在5次循环中&#xff0c;我们使用了同一个毫秒级的因子&#xff0c;从而生成相同的随机数&#xff0c;另外&#xff0c;第N&#43;1个数的生成与第N个数有着直接的关系。
在上面的例子中&#xff0c;假设系统启动以来的毫秒数为888毫秒&#xff0c;执行5次循环用时只有0.1毫秒&#xff0c;这导致在循环中创建的5个Random对象都使用了相同的888因子&#xff0c;每次被创建的随机对象又使用了相同的第N个数(默认为常数)&#xff0c;通过这样的假设我们不难看出&#xff0c;上面的结果是必然的。
现在我们改变这个格局&#xff0c;在循环之外创建一个Random对象&#xff0c;在每次循环中引用它&#xff0c;并通过它生成随机数&#xff0c;并在同一个对象上多次调用Next方法&#xff0c;从而不断变化第N个数&#xff0c;代码如下所示&#xff1a;
Random random &#61; new Random(); for (int i &#61; 0; i <5; i&#43;&#43;) { Console.WriteLine(random.Next()); }
执行后的结果如下所示&#xff1a;
391098894 1791722821 1488616582 1970032058 201874423
我们看到这个结果确实证实了我们上面的推断&#xff0c;第1次循环时公式中的第N个数为默认常数&#xff1b;当第二次循环时&#xff0c;第N个数为391098894&#xff0c;随后不断变化的第N个数作为因子参与计算&#xff0c;这保证了结果的随机性。
虽然通过我们的随机数看起来也很随机了&#xff0c;但必定这个算法是伪随机数&#xff0c;当第N个数和因子都相同时&#xff0c;生成的随机数仍然是重复的随机数&#xff0c;由于Random提供一个带参的构造函数允许我们传入一个因子&#xff0c;如果传入的因子随机性强的话&#xff0c;那么生成的随机数也会比较可靠&#xff0c;为了提供一个可靠点的因子&#xff0c;我们通常使用GUID产生填充因子&#xff0c;同样放在循环中测试&#xff1a;
for (int i &#61; 0; i <5; i&#43;&#43;) { byte[] buffer &#61; Guid.NewGuid().ToByteArray(); int iSeed &#61; BitConverter.ToInt32(buffer, 0); Random random &#61; new Random(iSeed); Console.WriteLine(random.Next()); }
这样的方式保证了填充因子的随机性&#xff0c;所以生成的随机数也比较可靠&#xff0c;运行结果如下所示&#xff1a;
734397360 1712793171 1984332878 819811856 1015979983
在一些场景下这样的随机数并不可靠&#xff0c;为了生成更加可靠的随机数&#xff0c;微软在System.Security.Cryptography命名空间下提供一个名为RNGCryptoServiceProvider的类&#xff0c;它采用系统当前的硬件信息、进程信息、线程信息、系统启动时间和当前精确时间作为填充因子&#xff0c;通过更好的算法生成高质量的随机数&#xff0c;它的使用方法如下所示&#xff1a;
byte[] randomBytes &#61; new byte[4]; RNGCryptoServiceProvider rngServiceProvider &#61; new RNGCryptoServiceProvider(); rngServiceProvider.GetBytes(randomBytes); Int32 result &#61; BitConverter.ToInt32(randomBytes, 0);
通过这种算法生成的随机数&#xff0c;经过成千上万次的测试&#xff0c;并未发现重复&#xff0c;质量的确比Random高了很多。另外windows api也提供了一个非托管的随机数生成函数CryptGenRandom&#xff0c;CryptGenRandom与RNGCryptoServiceProvider的原理类似&#xff0c;采用C&#43;&#43;编写&#xff0c;如果要在.NET中使用&#xff0c;需要进行简单的封装。它的原型如下所示&#xff1a;
BOOL WINAPI CryptGenRandom( _In_ HCRYPTPROV hProv, _In_ DWORD dwLen, _Inout_ BYTE *pbBuffer );
以上就是零度为您带来的随机数生成方法和基本原理&#xff0c;您可以通过需求和场景选择最佳的方式&#xff0c;Random算法简单&#xff0c;性能较高&#xff0c;适用于随机性要求不高的情况&#xff0c;由于RNGCryptoServiceProvider在生成期间需要查询上面提到的几种系统因子&#xff0c;所以性能稍弱于Random类&#xff0c;但随机数质量高&#xff0c;可靠性更好。
二.Guid 类
System.Guid
GUID (Globally Unique Identifier) 全球唯一标识符
GUID的计算使用到了很多在本机可取到的数字,如硬件的ID码,当前时间等.所计算出的128位整数(16字节)可以接近唯一的输出.
计算结果是xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx结构的16进制数字.当然这个格式也是可以更改的.
三.RNGCryptoServiceProvider 类
System.Security.Cryptography.RNGCryptoServiceProvider
RNGCryptoServiceProvider 使用加密服务提供程序 (CSP) 提供的实现来实现加密随机数生成器 (RNG)
因该类使用更严密的算法.所以即使如下放在循环中,所计算出的随机数也是不同的.
Membership.GeneratePassword()
Membership是一个方便快捷的进行角色权限管理的类,偶然发现一个很有意思的方法,没研究过是如何实现的
例:
结果为
C!&^HoTNv3!ZHkK9BAbu
azLgER)JJ-UW8q*14yz*
I3qnb]Zxu16ht!kKZ!Q*
9U:MAQ&c1x)^aed&#64;xe**
oL(%4JvfbP&t5*Hpl4l-
6&#64;zj$CnhW&D&#43;|xOf:qIk
A/!Di&l*tY$QaMH0gyzY
z^wu6{1BMq7D^&#43;WU]>f$
1OgIJS3&09fw0F9.|aXA
8F&#43;Gy&#43;L{O6x{SfugME*%