小仙,
关于:2.无参数的时候,以时间为种子值,不同时刻的数组都不相同,所以不必实例几个Random出来。但是要使用时错开时间制造一个时差。
这个地方有一点值得商榷。要使用随机数时,不必每次使用前都初始化Random对象实例,这个是对的。不过第二个,如果理解为:以一个时间差顺次初始化多个Random对象,所产生的随机数仍然有问题。
用Reflector来反查Random的方法,可以看到,Random的默认构造函数调用其带参的构造函数,传入参数是Environment.TickCount,表示系统从启动到现在所经过的毫秒数。
public Random() : this(Environment.TickCount)
{
}
我们再看带参数的构造函数:
public Random(int Seed)
{
this.SeedArray = new int[0x38];
int num2 = 0x9a4ec86 - Math.Abs(Seed);
this.SeedArray[0x37] = num2;
int num3 = 1;
for (int i = 1; i < 0x37; i++)
{
int index = (0x15 * i) % 0x37;
this.SeedArray[index] = num3;
num3 = num2 - num3;
if (num3 < 0)
{
num3 += 0x7fffffff;
}
num2 = this.SeedArray[index];
}
for (int j = 1; j < 5; j++)
{
for (int k = 1; k < 0x38; k++)
{
this.SeedArray[k] -= this.SeedArray[1 + ((k + 30) % 0x37)];
if (this.SeedArray[k] < 0)
{
this.SeedArray[k] += 0x7fffffff;
}
}
}
this.inext = 0;
this.inextp = 0x15;
Seed = 1;
}
它是用一个确定的数,减去你所给种子的绝对值。作为含有38项的int数组的最后一个元素,然后顺次生成余下的37个项。
初始分别生成两个不同的int值(虽不同,但是确定的),相当于指向数组,作为接下来产生随机数用。
然后我们看关键部分:InternalSample方法
private int InternalSample()
{
int inext = this.inext;
int inextp = this.inextp;
if (++inext >= 0x38)
{
inext = 1;
}
if (++inextp >= 0x38)
{
inextp = 1;
}
int num = this.SeedArray[inext] - this.SeedArray[inextp];
if (num < 0)
{
num += 0x7fffffff;
}
this.SeedArray[inext] = num;
this.inext = inext;
this.inextp = inextp;
return num;
}
这里,当调用Next方法时,总之会运行到这个方法中,这也是整个Random的核心,但是可以看出来,它也是简单的四则运算。
在整个Random中,除了种子,没有任何一个方法过程采用了非线性部件(普通的加减乘除四则运算都是线性运算,像诸如三角函数等是非线性运算),因此就不可能产生非线性随机数。整个随机数序列看似随机,但是却是线性的。
如果我们以默认构造函数构造对象,那么他所使用的TickCount毫无疑问也是线性的,且是严格按顺序递增的值。假如我们以1毫秒为间隔产生若干个Random,并且只调用一次Next,观察他们的值,你会发现他们的值甚至也是按+1排列顺次递增的。
这说明一个什么问题?如果以时间差为基础创建若干Random,然后使用一次Next随即销毁,则这个伪随机数倒更像是一个递增序列了。
所以靠时间差产生若干随机对象并产生随机数的方法仍然是不可行的。只能在需要使用随机数的最大范围内初始化对象,并在整个生命周期内仅使用该对象,不要随意或者按时间相关来创建销毁他。
还需要说明的问题是:按1毫秒为间隔产生随机对象这种做法只是一个理想情况而已,通常我们也模拟不出来,因为.net CLR运行时全权掌控了我们所使用对象的创建、销毁等工作,我们无法再.net中控制CLR在哪一个精确的时刻创建Random对象,因此会出现一个现象,有可能在看似方法的不同位置中创建的Random的代码,到了实际运行时却发现两个Random所产生的随机序列是一样的。这时因为运行时在同一时刻创建了他们,TickCount值没有发生变化。
所以再次说明,尽可能在最大范围内创建和使用Random,且Environment.TickCount是以毫秒计量的单位,而DataTime.Ticks则是以100毫微秒为计量单位,比TickCount细化了10000倍,Environment.TickCount是以操作系统启动开始到当前所经过的毫秒数,而Ticks则是从0001 年 1 月 1 日午夜 12:00:00 以来已经过的时间的以 100 毫微秒为间隔的间隔数,根据计算机产生中断信号的频率和Ticks的数据类型(long,64位有符号整数)来说,可以认为Ticks这个值永远不会重复。