热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#生成随机数的三种方法及其问题分析

本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。

随机数的定义为:产生的所有数字毫无关系.

在实际应用中很多地方会用到随机数,比如需要生成唯一的订单号.

在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*%



推荐阅读
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了在wepy中运用小顺序页面受权的计划,包含了用户点击作废后的从新受权计划。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
author-avatar
QEWERTGF_978
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有