当你在Stack Overflow网站标题中看到“随机”这个词你基本可以确定这是相同的基本问题无数的相似问题。本文带你探讨为什么随机性会引起这么多问题并且如何解决它们。

Stack Overflow (or newsgroup, or mailing list etc) )网站的问题通常是这样的:

我使用Random.Next生成随机数,但它一直给我相同的号码。 它不停的运行,但每次它会产生相同数量很多次。

这是由于这样的代码:

  // Bad code! Do not use!for (int i = 0; i < 100; i++){Console.WriteLine(GenerateDigit());}....static int GenerateDigit(){Random rng = new Random();// Assume there'd be more logic here reallyreturn rng.Next(10);}

那么,这程序到底出了什么问题?

1.解读

这种Random类不是真正的随机数发生器,它是一个伪随机数发生器。任何Random实例都有一定量的状态,而当你调用Next( or NextDouble or NextBytes),它会使用该状态来返回到似乎是随机的数据,相应的改变它内部状态以便于在下一步调用时你将得到另一个伪随机数。

所有的这一切都是确定的,如果你开始一个Random的实例以相同的初始状态(可通过种子来提供),并使用相同的序列方法调用它,那你会得到相同的结果。

那么在我们的示例代码中到底出了什么问题? 我们使用的一个新的Random实例也在循环迭代。随机无参数的构造函数取当前日期和时间作为种子-在内部定时器工作之前你通常可以执行大量代码,当前的日期和时间就会发生变化。 因此,我们重复使用相同的种子就会重复得到相同的结果。

2.对此我们能做什么?

这个问题有很多的解决方案, 其中有些方法是比其他的更好。 让我们先挑出其中一种方法,因为它不同于其他的方法。

3.使用加密的随机数发生器

  .NET有一个RandomNumberGenerator类应该是所有加密随机数生成器派生而来的抽象类。 这个框架本身附带了一个这样的派生类: RNGCryptoServiceProvider 。 加密随机数发生器的理念是,即使它可能仍然是一个伪随机生成器,它还是很难做到不可预料。 内置的实现需要多个熵源在你的电脑有效地呈现“噪音”,并难以预测。它可以使用这种噪音不仅仅是计算一个种子,也可以在生成下一个数字时让你知道当前的状态,这也许可能不足以预测下一个结果(或者那些已经生成),这主要取决于具体的实施。Windows也可以利用专业硬件资源的随机性(如一块硬件观察放射性同位素衰变),从而使得随机数发生器更加安全。

  相比于这种随机,如果你看到(说)10个结果调用Random.Next(100)并投入大量计算资源任务,你可能会制定出最初的种子并预知接下来的结果将是...很有可能也会知道之前的结果是什么。 如果这种随机数应用于证券或金融的目的,这会是灾难性的事态。 加密随机数生成器通常比Random慢 ,但它在赋予数字难以预测和独立方面做得更好。

  在很多情况下,随机数生成器的性能不是一个问题-但有一个适当的API就会出现问题。 随机数字生成器设计基础仅此是用来生成随机字节。比较这种API的随机 ,它可以让你请求一个随机整数,或随机double,或一组随机字节。我经常发现我需要一个整数的范围,得到可靠且一致地随机字节数组是很重要的。这不是不可能,但至少你可能会想要一个适配器类在随机数字生成器上。大多情况下,如果你能避免前面所述的陷阱,伪随机性的Random是可以接受的。

  让我们看看如何能做到这一点。

4.用一个复用的实例Random

对于“大量重复的数字”的修复程序的核心是重复使用同一个实例Random。 这听起来很简单...例如,我们可以改变我们这样原始的代码像这样:

// Somewhat better code...
Random rng = new Random();for (int i = 0; i < 100; i++){Console.WriteLine(GenerateDigit(rng));}...static int GenerateDigit(Random rng){// Assume there'd be more logic here reallyreturn rng.Next(10);}

  现在,我们的循环会打印不同的数字......但我们还没有完成。假如你在快速连续的时间内调用此代码会发生什么? 我们可能仍然需要创建的两个Random实例使用相同的种子......虽然数字的每个字符串将包含不同的数字,我们可以很容易得到的数字相同的字符串的两倍。

  有两种方式可以避免这个问题。 一种方式是使用一个静态字段保持的单个实例Random被每一个对象使用。另外,我们可以推高实例,当然是最终达到计划时,这永远只能实例化一个单一的元素随机性 ,并将其传递到任意地方。这是一个不错的主意(和它所表达的依赖性很好),但它不会完全的工作......至少,如果你的代码使用多个线程它会引发问题。

5.线程安全

  Random不是线程安全的。这是一个真正的痛处,因为考虑到我们观念上是想在任何程序中如何使用单个实例。 但事实是,如果你从多个线程使用相同实例,它很可能以全零内部状态结束,此时该实例变得无用。

  再次,在这里有两种方法可以解决这个问题。其一是仍然使用一个实例, 而且使用的每个调用方必须记住他们所使用的随机数生成器,同时获得锁。通过使用一个包装器锁定你就可以达到简化的效果,但在一个高度多线程系统中你仍然有可能浪费大量的时间等待加锁。

  在这里我们将学会另一种方法  - 是让每个线程有一个实例。 我们需要确保,当我们创建实例时我们不要重复使用相同的种子(例如,所以我们不能只调用无参数的构造函数),但除此之外它是相对简单的。

6.一个安全驱动

  很幸运的是,新ThreadLocal<T> .NET4类使得它很容易在每个线程需要单个实例中编写提供者。 您只需给ThreadLocal<T>构造一个委托调用来获得初始值当你不在的时候。 就我而言,我选择使用一个单一的种子变量,初始化使用Environment.TickCount(就像参数的Random构造函数),然后每递增,我们需要一个新的随机数生成器的时间-这是每一次的线程。

  整个类是静态的,只有一种公开方法: 随机获得线程 。这是一个方法而不是一个属性大多为方便起见:而不是让其中需要随机数的类依赖于Random本身,他们会依赖于Func<Random> 。 如果这类型仅设计在单个线程中运行,它可以调用委托获得的单个实例Random和重复使用; 假如它能够从多个线程中每次使用调用委托它就需要一个随机数发生器。 这将只会创造尽可能多的实例有线程,每个将使用不同的种子开始。 在依赖传球的时候,我们就可以用一个方法转换:

new TypeThatNeedsRandom(RandomProvider.GetThreadRandom) 下面的代码:

using System;using System.Threading;public static class RandomProvider{   private static int seed = Environment.TickCount;private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>new Random(Interlocked.Increment(ref seed)));public static Random GetThreadRandom(){return randomWrapper.Value;}}

  很简单,不是吗? 这是因为它的所有关注的是提供正确的Random实例 。 它并不在乎你采用什么样方法调用已经获取的实例。 代码仍然可以滥用这个类,当然,通过存放一个随机引用并用多个线程重复使用它,但要做对的事还是很容易的。

7.界面设计问题

  一个问题仍然存在:这依旧不是很安全的。 正如我前面提到的,最常用的派生类是RNGCryptoServiceProvider,还有一个更安全随机数字发生器的版本,然而这个API在一般情况下还是很难使用。

  假如框架驱动已经从“我想以简单的方法得到一个随机值”的概念中分离概念的“随机性源”,这确实是令人非常愉快的。 然后我们可以根据需要使用一个简单的API来支持一个安全的或不安全的随机源,很不幸的是,还没有这样的方法。也许在将来的迭代中......或者有个第三方会想出一个适配器来代替。(可惜这在我能力之上,很好地做好这件事情是相当困难的。)你几乎可以轻松成功地派生随机和覆盖示例及下个字节 ......但目前还不清楚他们需要如何工作,甚至Sample可能会非常棘手。 也许下一次...

  这是一篇国外的文章,被我翻译过来。原文地址:http://csharpindepth.com/Articles/Chapter12/Random.aspx

  接受批评指正,拒绝无脑喷粪。

转载于:https://www.cnblogs.com/david1989/p/3748109.html

解释杨中科随机数为什么会骗人?相关推荐

  1. 随机数是骗人的,.Net、Java、C为我作证 - 杨中科 - 博客园

    移动端全链路跟踪保障体系 随机数是骗人的,.Net.Java.C为我作证 - 杨中科 - 博客园

  2. 杨中科的.NET 6新书的出版进度汇报

    各位朋友好,我有3个月没发公众号了,今天打扰大家一下,不好意思. 最近这3个月,我在忙着给我的.NET 6新书收尾,并且在.NET 6正式版发布后,我把书中用.NET 6 预览版编写的内容在.NET ...

  3. 杨中科.NET5视频教程更新了:DI、配置系统、Logging、EF Core等

    我的.NET5视频教程又更新了一些内容了,包含依赖注入.配置系统.日志系统以及部分Entity Framework Core的内容.Entity Framework Core还没全讲完,会继续更新. ...

  4. 【杨中科】问苍天,微软的技术更新真的快吗

    经常在网站上看到有人抱怨: "微软的技术怎么更新这么快,.Net2.0.3.0.3.5.4.0.4.5,我的妈呀,都跟不上微软的步伐了!" "还没学会Silverligh ...

  5. 【解惑】杨中科说给弟弟的话

    刚才和我弟弟通电话聊了聊这一段时间的学习以及下一阶段的学习规划,我弟弟在读大二,是计算机专业,他也是如鹏网上一个比较活跃的会员.今天和他聊电话,感触颇多,倒不是因为我发现了新的东西,而是因为竟然连他都 ...

  6. 自学杨中科老师视频 68.Part3-19:EF Core自引用的组织结构树(Av500515272,P68) 出现错误

    自学杨中科老师视频 68.Part3-19:EF Core自引用的组织结构树(Av500515272,P68) 出现错误 将方法改为如下

  7. 杨中科 多线程 生成索引_遇见植物,发现精彩首届中科院核心植物园青年科学节暨版纳植物园第五届青年科学节举行...

    青年科学节开幕式现场 11月1日至2日,以"遇见植物"为主题的首届中科院核心植物园青年科学节暨版纳植物园第五届青年科学节在版纳植物园环境教育中心举行.版纳植物园党委书记.副主任杨永 ...

  8. 杨中科老师谈C语言指针问题快速解惑

    1. int a[3] = {2,5,8}; int* p = a; 把数组名赋值给指针表示将数组的首元素的地址赋予此指针. 2. int a[3] = {2,5,8}; int* p = & ...

  9. Python学习之---杨辉三角的五种解法

    杨辉三角(也称帕斯卡三角)对与编程初学者来说,肯定不陌生,它是一个无限对称的数字金字塔,从顶部的单个1开始,下面一行中的每个数字都是上面两个数字的和. 杨辉三角,是二项式系数在三角形中的一种几何排列, ...

  10. 模拟微信抢红包demo,生成随机数

    文章目录 概述 随机数方法方法 Math.random()方法 Random类 抢红包!! 概述 经常抢红包会发现,很大的概率是在一开始得时候抢的红包越大,越靠后越小(大概率是这种情况,这是我的经验之 ...

最新文章

  1. 最全面的Unity游戏开发指南视频教程 第2卷
  2. 2019年机器学习市场潜力大盘点(附27份资料完整版)
  3. tkinter的GUI设计:界面与逻辑分离(三)-- 多页面
  4. html:(25):选择器定义和标签选择器
  5. 665C. Simple Strings
  6. android 获取mac地址
  7. Codeforces 1077F2 Pictures with Kittens (hard version)(DP+单调队列优化)
  8. 【docker】docker持续集成CI/持续部署CD
  9. redis scan命令详解
  10. 《最新黑客攻防实战从入门到精通(第2版)》.(武新华, 孙振辉 ).[PDF]ckook
  11. 员工评分系统现场发布小感
  12. java调试步骤_Java程序的开发过程及基本调试方法
  13. 关于电子账户开户四五要素
  14. MessageBox confirm弹框确认和取消按钮的使用-回调
  15. python+fastapi+jinja2+mongodb,突然感觉整个人一下就轻松了,python学习之路
  16. 吃什么怎么吃关系着民族的命运
  17. 服务器导出excel文档,服务器导出excel功能配置
  18. 安装配置DOSBox
  19. win10 vs2015 jsoncpp编译 支持xp系统
  20. delphiXE关于线程和多线程、线程的同步与异步执行

热门文章

  1. 脑电EEG代码开源分享 【2.预处理-静息态篇】
  2. Keil MDK4及MDK5的下载及注册 STM32F10x 标准外设库的下载
  3. 项目管理过程标准及绩效考核
  4. 使用 POI 读取 Word docx 中的书签、替换书签内容(汉字或合并外部文档内容)
  5. Java实现中英文词典功能
  6. 三角形箭头向右的_向右三角形特殊符号
  7. 结构可靠性分析中响应面方法的基本思想
  8. Word VBA:批量转PDF且保留书签
  9. 为什么chrome视频时卡得厉害(by quqi99)
  10. 局域网内如何实现远程桌面控制