C#中生成的随机数为什么不随机?
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 );
以上就是零度为您带来的随机数生成方法和基本原理,您可以通过需求和场景选择最佳的方式,Random算法简单,性能较高,适用于随机性要求不高的情况,由于RNGCryptoServiceProvider在生成期间需要查询上面提到的几种系统因子,所以性能稍弱于Random类,但随机数质量高,可靠性更好。
C#中生成的随机数为什么不随机?的更多相关文章
- sql语句中生成0-10随机数
DECLARE @i int=0;DECLARE @j decimal(9,2);DECLARE @qnum INT=1000; SET NOCOUNT ONCREATE TABLE #temp_Ta ...
- Oracle中生成随机数的函数(转载)
在Oracle中的DBMS_RANDOM程序包中封装了一些生成随机数和随机字符串的函数,其中常用的有以下两个: DBMS_RANDOM.VALUE函数 该函数用来产生一个随机数,有两种用法: 1. 产 ...
- JS生成指定范围内的随机数(支持随机小数)
直接需要函数的话,直接到文章的最后面找. ============================================================= 转载:https://www.cn ...
- 高并发分布式系统中生成全局唯一(订单号)Id js返回上一页并刷新、返回上一页、自动刷新页面 父页面操作嵌套iframe子页面的HTML标签元素 .net判断System.Data.DataRow中是否包含某列 .Net使用system.Security.Cryptography.RNGCryptoServiceProvider类与System.Random类生成随机数
高并发分布式系统中生成全局唯一(订单号)Id 1.GUID数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么通过组合的方式,保留GUID的10个字节,用另6个字节表示GUID生成的时间(D ...
- 在Sqlserver中生成随机数据
百度了各种随机生成,集中摘录如下: 一.循环写入千万级测试数据 DECLARE @i int ) BEGIN INSERT INTO A_User(username,password,addtime, ...
- Python中生成随机数
目录 1. random模块 1.1 设置随机种子 1.2 random模块中的方法 1.3 使用:生成整形随机数 1.3 使用:生成序列随机数 1.4 使用:生成随机实值分布 2. numpy.ra ...
- ios 中生成随机数
ios 有如下三种随机数方法: 1. srand((unsigned)time(0)); //不加这句每次产生的随机数不变 int i = rand() % 5; 2. s ...
- JAVA中生成指定位数随机数的方法总结
JAVA中生成指定位数随机数的方法很多,下面列举几种比较常用的方法. 方法一.通过Math类 public static String getRandom1(int len) { int rs = ( ...
- Oracle中生成随机数的函数
在Oracle中的DBMS_RANDOM程序包中封装了一些生成随机数和随机字符串的函数,其中常用的有以下两个: DBMS_RANDOM.VALUE函数 该函数用来产生一个随机数,有两种用法: 1. 产 ...
随机推荐
- 拳打Adam,脚踢SGD:北大提出全新优化算法AdaBound
https://mp.weixin.qq.com/s/el1E-61YjLkhFd6AgFUc7w
- acid. cap
BASE是下面三个术语的缩写: 基本可用(Basically Available) 软状态(Soft state) 最终一致(Eventually consistent) 目前最快的KV数据库,10W ...
- Copycat - configure
Copycat server之间的configure是如何,何时被同步的? 大家可以看到,只有leader可以同步配置 1. 显式的调用LeaderState.configure Leader ...
- 苹果审核被拒,Guideline 1.1.6 - Safety - Objectionable Content;Guideline 3.1.1 - Business - Payments - In-App Purchase
Guideline 1.1.6 - Safety - Objectionable Content Thank you for your resubmission. We noticed that yo ...
- odoo10如何自定义自动生成单据编号
1.在已有的model中穿件一个字段name class qingjiadan(models.Model): _name = 'qingjia.qingjiadan' name = fields.Ch ...
- 《HTTP - http2.0》
推荐一首歌 - <卡路里>火箭101 杨超越真的只唱了一句! PS: 这章主要讲述了在 HTTP 发展之后的,一些扩展协议和补充,也会在这个章节对这几种协议和补充做一个比较. 1:HTTP ...
- tomcat在Eclipse中和idea中的使用
在eclipse中的使用 下载 http://tomcat.apache.org/ 部署项目到tomcat 常见问题 访问时如何出掉项目名 中文乱码问题 1.浏览器编码问题,修改浏览器的编码 2.js ...
- python摸爬滚打之day02----while循环,运算符,格式化输出
1.while循环 1.1 结构:while +条件判断: while 循环体 else: 条件不成立时语句块 while...else...是一个循环整体,当循环条件成立时执行while循环体内容 ...
- java 网络编程(一)InetAddress
package cn.sasa.net; import java.net.InetAddress; import java.net.UnknownHostException; public class ...
- Java设计模式之模板模式及使用场景
模板模式,顾名思义,就是通过模板拓印的方式. 定义模板,就是定义框架.结构.原型.定义一个我们共同遵守的约定. 定义了模板,我们的剩余工作就是对其进行充实.丰润,完善它的不足之处. 定义模板采用抽象类 ...