您好,欢迎来到九壹网。
搜索
您的当前位置:首页用伪随机数生成器Random生成随机数序列

用伪随机数生成器Random生成随机数序列

来源:九壹网
⽤伪随机数⽣成器Random⽣成随机数序列

在程序设计过程中,我们经常需要⽤到不同的随机数序列,于是我们写下了这样的程序://TickCount.CS

public class MainClass{

public static void Main() {

for(int i=0; i<10; i++)//⽣成10个随机序列 {

CreateRand(); } }

public static void CreateRand() {

Random random = new Random(); for(int i=0;i<6;i++)//6个数字的随机序列

Console.Write(string.Format(\"{0} \ Console.WriteLine(); }}

然⽽不幸的是,我们却得到了⼏个相同的序列,在我的计算机上(AMD 1.49GHz CPU,512M内存,WinXp+Sp2)编译和运⾏结果如下:E:CSC>csc tickcount.cs

Microsoft (R) Visual C# 2005 编译器 版本 8.00.50727.42

⽤于 Microsoft (R) Windows (R) 2005 Framework 版本 2.0.50727版权所有 (C) Microsoft Corporation 2001-2005。保留所有权利。E:CSC>tickcount.exe

733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 1887876763733598421 19040840 330766232 1331029161 1336728080 18878767631215178376 1762749978 309033493 16197137 2942275 132733301215178376 1762749978 309033493 16197137 2942275 13273330

前8个“随机”序列是相同的,后2个“随机”序列也是相同的,这不是我们希望得到的结果,我们希望得到的是互不相同的随机数序列!为什么会得到相同的序列呢?究其原因,就是Random类只是⼀个伪随机数⽣成器,并不能产⽣\"绝对随机\"的随机数。这⾥“伪”不是表⽰“假”的意思,⽽是表⽰“有规律”的意思,也就是说:计算机产⽣的伪随机数既是随机的⼜是有规律的。

伪随机数(有库函数产⽣)与“理想中的”“真”随机数不同,伪随机数是由可确定的(deterministic)函数产⽣,虽然随机函数可以产⽣有随机特征的数字序列,但这些数字并不不具备真随机数的⼀些特性,并⾮统计意义上的随机数。伪随机数是可以确定的:知道序列中的⼀个数就可以获得其他剩下数字的有关信息;事实上,如果知道了序列的初始值(种⼦)通常可以确定整个序列。记得⼤⼀上计算机专业基础课的第⼀节课上,⽼师就给我们介绍了计算机程序的5个特性(详见附1),其中的⼀点就是确定性,即“对于相同的输⼊只能得出相同的输出”,伪随机数的⽣成正是符合这条⾦科⽟律。下⾯我们来观察伪随机数⽣成器Random的两个构造函数: public Random() : this(Environment.TickCount){}//默认构造函数,以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; } }

}//根据程序的确定性,显然传⼊相同的Seed,将得到同样的SeedArray this.inext = 0;

this.inextp = 0x15; Seed = 1;}

Random类的默认构造函数中,以系统启动后经过的毫秒数TickCount作为随机数种⼦,在MSDN中,针对Environment.TickCount有这样⼀句注释:“TickCount 属性的分辨率不能⼩于500毫秒。”。也就是说:在0.5秒之内,我们将得到相等的TickCount值;再啰嗦点⼉就是说:在0.5秒之内通过默认的构造函数构造的所有Random实例都有相同的SeedArray,相同的数据源在同⼀台计算机上经过相同的算法处理后,理所当然应该得出相同的结果序列了^_^。

虽然MSDN中说“TickCount属性的分辨率不能⼩于500毫秒”,但我在⾃⼰的机器上测试了⼀下,结果略有偏差,下⾯是测试代码和编译运⾏结果:

//TickCount2.CSusing System;

public class MainClass{

public static void Main() {

long start;

long end = start = Environment.TickCount;

while(end == start) //测试系统能分辨的TickCount的取值间隔 {

end = Environment.TickCount; }

Console.Write(\"Ticks:\");

Console.WriteLine(end - start); DateTime startTime;

DateTime endTime = startTime = DateTime.Now;

while(endTime == startTime)//测试系统能分辨的DateTime的取值间隔 {

endTime = DateTime.Now; }

Console.Write(\"Time:\");

Console.WriteLine(endTime - startTime); }}

//在我的计算机上编译和运⾏结果如下:E:CSC>csc tickcount2.cs

Microsoft (R) Visual C# 2005 编译器 版本 8.00.50727.42

⽤于 Microsoft (R) Windows (R) 2005 Framework 版本 2.0.50727版权所有 (C) Microsoft Corporation 2001-2005。保留所有权利。E:CSC>tickcount2.exeTicks:10

Time:00:00:00.0100144

啊哈,我的机器上TickCount的分辨率约为10毫秒(0.0100144秒)^_^

说了这么多,我们了解到在⼀段很⼩的时间段内(我的机器上约是10毫秒),由默认构造函数⽣成的不同Random实例将产⽣相同的“随机”数(或“随机”数序列)。现在回到⽂章开头的问题上,如果我们想在很短的时间内得到不同的“随机”数序列,该怎么办呢?⼀个解决办法就是在短时间内不要构造不同的Random实例,⽽是使⽤同⼀个Random实例,代码⽰例(对TickCount1.CS稍加修改)和编译运⾏结果如下:

//TickCount3.CSusing System;

public class MainClass{

public static void Main() {

Random random = new Random();

for(int i=0; i<10; i++)//⽣成10个随机序列 {

CreateRand(random); } }

public static void CreateRand(Random random) {

for(int i=0;i<6;i++)//6个数字的随机序列

Console.Write(string.Format(\"{0} \ Console.WriteLine(); }}

/// 程序运⾏编译和运⾏结果如下:E:CSC>csc tickcount3.cs

Microsoft (R) Visual C# 2005 编译器 版本 8.00.50727.42

⽤于 Microsoft (R) Windows (R) 2005 Framework 版本 2.0.50727版权所有 (C) Microsoft Corporation 2001-2005。保留所有权利。E:CSC>tickcount3.exe

1881514665 187597856 1876537022 2146319285 138059684 807943728576262772 779063600 1237870363 742301708 2044162198 16948554122005040916 325723903 736394631 133990481 1511650304 1694759686369588 305867187 1159984149 953908396 16854992 74108171282574331 555617587 1241463657 736349125 596033346 794494845780261119 120040711 15190931 565960940 45469235 15970980331712262257 825399914 282791381 39071 1341257075 448146946633252151 635828560 2022035278 1352429249 463708613 1214940829439459392 16630291 7518087 1823526590 2925208 19085037761336657042 927606269 9581861 1135472205 863744954 1729925744

这样的结果“从⼀定程度上”符合了“在短时间内⽣成不同的随机数序列”,然⽽,事情远没有这么简单:这⾥我⽤引号强调了只是从“⼀定程度上”符合⽽已,实际上并不完全符合“随机”的要求。要想完全符合“随机”的要求(例如在加密程序中),必须使⽤真随机数!真随机数与伪随机数相反,其⽣成过程必须独⽴于它的⽣成函数,所以即使知道了真随机数序列中的⼀些数,外界也⽆法推算序列中的其他数。

附1 - 计算机程序的5个特性:

有穷性:⼀个算法必须总是(对任何合法的输⼊值)在执⾏有穷步之后结束,且每⼀步都可在有穷时间内完成;

确定性:算法中每⼀条指令必须有确切的含义,读者理解时不会产⽣⼆义性。有任何条件下,算法只有唯⼀的⼀条执⾏路径,即对于相同的输⼊只能得出相同的输出。

可⾏性:⼀个算法是能⾏的,即算法中描述的操作都是可以通过已经实现的基本运算执⾏有限次来实现的。 输⼊:⼀个算法有零个或多个的输⼊,这些输⼊取⾃于某个特定的对象的集合。 输出:⼀个算法有⼀个或多个的输出。这些输出是同输⼊有着某些特定关系的量。

附2 - Random类源代码(经Reflector.exe反汇编得到):

namespace System{

using System.Globalization;

using System.Runtime.InteropServices; [Serializable, ComVisible(true)] public class Random {

private int inext; private int inextp;

private const int MBIG = 0x7fffffff;//0111 1111 1111 1111 1111 1111 1111 1111

private const int MSEED = 0x9a4ec86;//0000 1001 1010 0100 1110 1100 1000 0110 private const int MZ = 0; private int[] SeedArray;

public Random() : this(Environment.TickCount) { } /*

* Environment.TickCount⽅法 * public static int TickCount * { * get * {

* return nativeGetTickCount(); * } * } *

* [MethodImpl(MethodImplOptions.InternalCall)] * private static extern int nativeGetTickCount(); */

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; } }

}//传⼊相同的Seed,将得到同样的SeedArray this.inext = 0;

this.inextp = 0x15; Seed = 1; }

private double GetSampleForLargeRange() {

int num = this.InternalSample();

if ((((this.InternalSample() % 2) == 0) ? 1 : 0) != 0) {

num = -num; }

double num2 = num; num2 += 21474836;

return (num2 / 4294967293); }

private int InternalSample() {

int index = this.inext; int inextp = this.inextp; if (++index >= 0x38) {

index = 1; }

if (++inextp >= 0x38) {

inextp = 1; }

int num = this.SeedArray[index] - this.SeedArray[inextp]; if (num < 0) {

num += 0x7fffffff; }

this.SeedArray[index] = num; this.inext = index; this.inextp = inextp; return num; }

public virtual int Next() {

return this.InternalSample(); }

public virtual int Next(int maxValue) {

if (maxValue < 0)

{

throw new ArgumentOutOfRangeException(\"maxValue\string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString(\"ArgumentOutOfRange_MustBePositive\"), new object[] { \"maxValue\" })); }

return (int) (this.Sample() * maxValue); }

public virtual int Next(int minValue, int maxValue) {

if (minValue > maxValue) {

throw new ArgumentOutOfRangeException(\"minValue\string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString(\"Argument_MinMaxValue\"), new object[] { \"minValue\ }

long num = maxValue - minValue; if (num <= 0x7fffffff) {

return (((int) (this.Sample() * num)) + minValue); }

return (((int) ((long) (this.GetSampleForLargeRange() * num))) + minValue); }

public virtual void NextBytes(byte[] buffer) {

if (buffer == null) {

throw new ArgumentNullException(\"buffer\"); }

for (int i = 0; i < buffer.Length; i++) {

buffer[i] = (byte) (this.InternalSample() % 0x100); } }

public virtual double NextDouble() {

return this.Sample(); }

protected virtual double Sample() {

return (this.InternalSample() * 4.6566128752457969E-10); } }}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务