随机数,接触程序时就会接触到他,第一次知道随机数隐约记得是和同学在讨论彩票摇奖时知道的,现在看着自己那一把厚厚的彩票就知道当时有多天真了,天真的以为那玩意真随机。好了,言归正传。
几乎我接触过的每一种语言都提供产生随机数的系统方法,我平时用的时候直接拉来用了,今天突然看见群里有人在问,就想着那就研究到底,看看random到底是个什么鬼。
产生随机数的方法:
Random属于system下的类,想仔细研究,那就F12戳进去,看看他都有什么方法。
Next()方法用来返回一个随机数。同样的代码你执行和我的结果很可能不一样,而且我多次运行的结果也很可能不一样,这就是随机数。
1、不是bug的bug:
当我想使用上面的方法生成多个随机数时,我自然而然的想到了for循环,于是我就循环一百次,生成100个随机数,但结果让我有点卵碎。
<pre name="code" class="csharp">if (Input.GetKeyDown(KeyCode.T))
{
for (int i = 0; i < 100; i++)
{
System.Random rand = new System.Random();
Debug.Log("随机数 = " + rand.Next());
}
}
你会发现,出现了大量的重复数字。这还真是见了鬼了。于是我想着rand就是个对象,为什么要每次去实例化,看网上说可以把实例化的代码放在for循环外边,试了试:
if (Input.GetKeyDown(KeyCode.T))
{
System.Random rand = new System.Random();
for (int i = 0; i < 100; i++)
{
Debug.Log("随机数 = " + rand.Next());
}
}
还真欧克了
2、Why出现重复的现象:
这得扯一扯计算机生成随机数的原理了,隐约记得之前了解过“随机种子”的概念,于是百度了下,原理是生成随机数的算法有很多种,最简单也是最常用的就是 "线性同余法": 第n+1个数=(第n个数*29+37) % 1000,而“第n个数”就称之为随机种子,而重复的根本原因就是默认的随机种子是CPU时钟,现在硬件这么发达,一微妙执行几遍函数都不是问题,所以同一时间段,出现多个重复的随机数也就能理解了。我用代码解释一下公式吧,MyJiLeiRandom是我自定义的一个功能极其鸡肋的生成随机数的类,你调用Next()就会给你生成鸡肋的随机数:
public class MyJiLeiRandom
{
private int seed = 2;
public int Next()
{
int next = (seed * 29 + 37) % 1000;
seed = next;
return next;
}
}
他的确看起来是随机的:
那我们平时怎么让这个鸡肋的随机数真的很随机呢,其实有很多办法,比如让随机种子等于游戏自打开以来运行的时长、当前用户鼠标在屏幕上的坐标、设置本机处理器ID+启动时长-鼠标坐标.x*鼠标坐标.y。各种方法,就怕你把自己也搞随机了。
用场景加载时长来作为“随机种子”,来产生看似“真正”的随机数。
if (Input.GetKeyDown(KeyCode.Y))
{
MyJiLeiRandom myRand = new MyJiLeiRandom();
myRand.seed = (int)Time.timeSinceLevelLoad * Screen.currentResolution.height;
for (int i = 0; i < 100; i++)
{
Debug.Log("鸡肋类随机数 = " + myRand.Next());
}
}
关于真随机数,看过一个帖子,它确实生成了,它主要依据当前进程Id、当前线程Id、系统启动后的TickCount、当前时间、QueryPerformanceCounter返回的高性能计数器值、用户名、计算机名、CPU计数器的值等等来计算。不过一般的项目上确实用不到,如果要用,可自行百度。但万变不离其宗,无非是把“随机种子”搞的几乎不可能重复。