C#产生不重复的随机数并生成随机文件名

本文转自:http://blog.ciznx.com/post/csharprandomnumberandrandomfilename.aspx算法

在项目中会遇到须要批量生成文件的时候,好比 asp.net 中生成静态文件、在 Winform 项目中命名临时文件等,这时咱们考虑使用随机名称,也就不可避免地须要使用到 .Net 提供的 System.Random 类。对实际上,计算机不 可能产生彻底随机的数字,所谓的随机数发生器都是经过必定的算法对事先选定的随机种子作复杂的运算,用产生的结果来近似的模拟彻底随机数,这种随机数被称 做伪随机数。Random 类自身在 Intellisense 中也表述为“伪随机数”。伪随机数是以相同的几率从一组有限的数字中选取的。所选数字并不具备彻底的随机性,可是从实用的角度而言,其随机程度已足够了。
 多线程

伪随机数的选择是从随机种子开始的,因此为了保证每次获得的伪随机数都足够地“随机”,随机种子的选择就显得很是重要。若是随机种子同样,那么同一个随机数发生器产生 的随机数也会同样。通常地,咱们使用同系统时间有关的参数做为随机种子,若是不给 Random 的构造函数传递参数,那 Random 会以当前系统时间由特定算法算得一个种子,这是.net Framework中的随机数发生器默认采用的方法。 也就是说,若是时间相同(纵使时间永远不会相同,但在CPU高速运行的状态下,系统获取到的时间是有可能相同的。),即便使用不一样的 Random 实例,因为初始化它们的种子(系统时间)是相同的,因此Random 对象的这些实例产生的随机数仍然是相同的。这一点在实验中获得证明,实验代码以下:并发

using System;  
  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            for (int i = 0; i < 100; i++)  
            {  
                Console.WriteLine(RandomNumber());  
            }  
            Console.WriteLine("Press any key to continue...");  
            Console.ReadLine();  
        }  
  
        static int RandomNumber()  
        {  
            Random rand = new Random();   
            return rand.Next(1000);  
        }  
  
    }  

在控制台输出的结果中,咱们能够看到,接连输出的结果会相同,而后再变化,而后继续相同,再变化……即不规律地间歇性地变化。这个问题在 asp.net 程序中尤为明显,由于 asp.net 程序自然是多线程的,常常会遇到高并发,因此若是一个生成文件的操做由很高的并发一块儿执行,就可能因为生成的文件名相同而形成 IO 错误,或者相互覆盖。因此生成不重复的随机数(即“随机”的随机数)一直是一个受关注的技术话题。
其实 .Net  自己也已经提供了一些类来提供这样的支持,好比 System.Security.Cryptography.RNGCryptoServiceProvider 及 CryptGenRandom() 等

对于随机数的重复部分,有几种方法能够方便地解决。好比上述代码,能够简单地将 rand 对象的声明放在类里,做为类的静态字段来对待,而后在 RandomNumber 方法里调用它,也就是把上述代码中的 RandomNumber 代码改成:
static Random rand = new Random();   
static int RandomNumber()  
{  
    return rand.Next(1000);  
}  

这样就能够解决问题了,这样同样,每次输出的随机数都会不相同:由于 rand 对象只用种子初始化了一次。

在日常的开发中,不少人习惯于将一些零散的方法都集中起来放在一个静态类中(如 Utils),咱们但愿诸如此类的方法在一个方法中就搞定,而上述方法中将字段 rand 的声明放到了方法的外面,这样破坏了代码的整洁性,在代码重用的时候也容易遗漏,因此仍是要考虑把实现这功能的代码放在同一个方法中,这仍是要面对这个问题:“若是在相同的时间得到不一样的随机种子”。咱们知道 Guid 类型的实例是全局不重复的值类型对象,所以使用它能够方便的做为独立的种子,即每次调用方法都使用一个从 Guid 的二进制值转换获得的数字做为种子。示例代码以下:
 
static int RandomNumber()  
{  
    Guid randSeedGuid = Guid.NewGuid();  
    Random rand = new Random(BitConverter.ToInt32(randSeedGuid.ToByteArray(),0));  
    return rand.Next(1000);  
}  

 

 
若是以为这个办法的效率多是个问题的话,也能够受此启发,而使用其余类型的实例的 GetHashCode() 的返回值做为种子。
此外,在 System.Security.Cryptography 命名空间中提供了  RNGCryptoServiceProvider 类用于提供随机支持,能够用它来做为随机器的初始化种子能够用于生成随机的随机数(具体的例子,请参照 MSDN 的示例代码) 对于上述几种方法,读者您能够自行试验其效率并作对比。 好了,第一个问题解决了,那文件名呢?其实解决了第一个问题,那问题也就解决了一大半了,由于只要在同一时间产生不一样的随机数,那即便是多线程也不怕了;不过还有一个问题,文件系统不容许同名的文件,所以除了要保证本次生成的文件名要不相同,还要保证与以前已经存在的文件名不相同。这也不是什么难题,使用 System.IO.File.Exist() 方法便可验证是否已存在相同的文件名,使用一个 while 循环来一直获取随机文件名,直到不与已经存在的文件名相同为止。
相关文章
相关标签/搜索