现在的位置: 首页 > 综合 > 正文

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

2011年07月15日 ⁄ 综合 ⁄ 共 4420字 ⁄ 字号 评论关闭

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

//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} ",random.Next()));
        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 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
733598421 1904084089 330766232 1331029161 1336728080 1887876763
1215178376 1762749978 309033493 1619713897 294892275 1327336430
1215178376 1762749978 309033493 1619713897 294892275 1327336430

    前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.CS
using 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.exe
Ticks:
10
Time:
00:00:00.0100144

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

 

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

//TickCount3.CS
using System;
public class MainClass

抱歉!评论已关闭.