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



推荐阅读
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 微软Exchange服务器遭遇2022年版“千年虫”漏洞
    微软Exchange服务器在新年伊始遭遇了一个类似于‘千年虫’的日期处理漏洞,导致邮件传输受阻。该问题主要影响配置了FIP-FS恶意软件引擎的Exchange 2016和2019版本。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 本文介绍了如何利用npm脚本和concurrently工具,实现本地开发环境中多个监听服务的同时启动,包括HTTP服务、自动刷新、Sass和ES6支持。 ... [详细]
  • 本文探讨了 Spring Boot 应用程序在不同配置下支持的最大并发连接数,重点分析了内置服务器(如 Tomcat、Jetty 和 Undertow)的默认设置及其对性能的影响。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
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社区 版权所有