热门标签 | 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*%



推荐阅读
  • switch语句的一些用法及注意事项
    本文介绍了使用switch语句时的一些用法和注意事项,包括如何实现"fall through"、default语句的作用、在case语句中定义变量时可能出现的问题以及解决方法。同时也提到了C#严格控制switch分支不允许贯穿的规定。通过本文的介绍,读者可以更好地理解和使用switch语句。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了在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启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
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社区 版权所有