C#生成随机数之二 生成不重复的随机数

来源:互联网 发布:java加密解密工具类 编辑:程序博客网 时间:2024/05/10 15:10

用 C# 生成不重复的随机数的三种方法

第一种方法:利用HashTable

/// <summary>/// 利用Hashtable/// </summary>static int[] UseHashTableToNonRepeatedRandom(int length, int minValue, int maxValue){    Hashtable hashtable = new Hashtable();    int seed =  Guid.NewGuid().GetHashCode();    Random random = new Random(seed);    for (int i = 0; hashtable.Count < length; i++)    {        int nValue = random.Next(minValue, maxValue);        if (!hashtable.ContainsValue(nValue) && nValue != 0)        {            hashtable.Add(i, nValue);            //hashtable.Add(nValue, nValue);        // 将 key 和 value设置成一样的值,导致hashtable无法按添加顺序输出数组            //Console.WriteLine(nValue.ToString());        }    }    int[] array = new int[hashtable.Count];    hashtable.Values.CopyTo(array, 0);    return array;}

注意事项:
1.该方法 length 和 maxValue的相关性比较强,使用时应该予以着重考虑
2.方法不适合生成 maxValue == length 相等的数据,
  例如:不适合生成有10道考题的不重复题目号(要求从1-10,随机生成且不重复)
  maxValue最好不等于length参数,
  当 maxValue - minValue = length 时,时间效率差一个数量级【主要由于for循环内的生成语句导致】
  实际测试生成1w个数 maxValue - minValue 是 length 的
  2倍 :生成时间(使用7倍时)翻倍
  7倍及以上:生成时间基本一致
3.使用HashTable保存 key 和 value 时,不要将同一个数值存入key和value,否则将导致数据顺序输出给数组
注意:
  生成时间和具体使用的PC几性能有关,仅做参考!

效率说明:1w条数据测试
HashTable方法当ArrayNum_=_maxValue时
这里写图片描述

HashTable方法当maxValue是ArrayNum的1000倍时
这里写图片描述


第二种方法:递归(使用递归检测重复数)

/// <summary>/// 检查生成的随机数据是否重复/// 如果重复则继续递归调用/// 如果不重复则返回该数/// </summary>/// <param name="arr">生成的非重复随机数数组</param>/// <param name="temp">随机数</param>/// <param name="minValue">最大值</param>/// <param name="maxValue">最小值</param>/// <param name="random">生成随机数对象</param>/// <returns>返回一个不重复的随机数</returns>static int getNumberNonRepeatedRandom(int[] arr, int temp, int minValue, int maxValue, Random random){    int n = 0;    while (n <= arr.Length - 1)    {        if (arr[n] == temp) //利用循环判断是否有重复         {            temp = random.Next(minValue, maxValue); //重新随机获取。             getNumberNonRepeatedRandom(arr, temp, minValue, maxValue, random);//递归:如果取出来的数字和已取得的数字有重复就重新随机获取。         }        n++;    }    return temp;}/// <summary>/// 递归,用它来检测生成的随机数是否有重复,如果取出来的数字和已取得的数字有重复就重新随机获取。/// </summary>static int[] RecursiveMethodToNonRepeatedRandom(int length, int minValue, int maxValue){    int seed = Guid.NewGuid().GetHashCode();    Random random = new Random(seed);    int[] array = new int[length];    int temp = 0;    for (int i = 0; i < length; i++)    {        temp = random.Next(minValue, maxValue); // 随机取数         array[i] = getNumberNonRepeatedRandom(array, temp, minValue, maxValue, random); // 取出值赋到数组中     }    return array;}

注意事项:
使用条件:
1.maxValue - minValue = length。
  若差值过于接近或等于length将导致 Stack Overflow Excepion 异常
  该异常一般是因为函数调用栈溢出,
  也就是函数调用的层次超出了程序能够接受的范围.
  这种情况通常是由函数循环调用导致无限递归而引起的
2.实际测试生成1w个数 maxValue 是 length 的
  2倍及以上:生成时间基本一致

效率说明:1w条数据测试

maxValue - minValue = length 若差值过于接近或等于length将导致 Stack Overflow Excepion 异常
这里写图片描述

实际测试生成1w个数 maxValue 是 length 的 2倍及以上:生成时间基本一致
这里写图片描述


第三种方法:使用双数组策略

  【思路】:是用一个数组来保存索引号,先随机生成一个数组位置,然后把随机抽取到的位置的索引号取出来,并把最后一个索引号复制到当前的数组位置,然后使随机数的上限减一。
  具体如:先把这100个数放在一个数组内,每次随机取一个位置(第一次是1-100,第二次是1-99,…),将该位置的数用最后的数代替。

/// <summary>/// 方法一 使用随机抽取数组index中的数,填充在新的数组array中,使数组array中的数是随机的/// 方法一思路:用一个数组来保存索引号,先随机生成一个数组位置,然后把随机抽取到的位置的索引号取出来,///             并把最后一个索引号复制到当前的数组位置,然后使随机数的上限减一,具体如:先把这100个数放在一个数组内,///             每次随机取一个位置(第一次是1-100,第二次是1-99,...),将该位置的数用最后的数代替。/// </summary>static int[] UseDoubleArrayToNonRepeatedRandom(int length, int minValue, int maxValue){    int seed = Guid.NewGuid().GetHashCode();    Random radom = new Random(seed);    int[] index = new int[length];    for (int i = 0; i < length; i++)    {        index[i] = i + 1;    }    int[] array = new int[length]; // 用来保存随机生成的不重复的数     int site = length;             // 设置上限     int idx;                       // 获取index数组中索引为idx位置的数据,赋给结果数组array的j索引位置    for (int j = 0; j < length; j++)    {        idx = radom.Next(0, site - 1);  // 生成随机索引数        array[j] = index[idx];          // 在随机索引位置取出一个数,保存到结果数组         index[idx] = index[site - 1];   // 作废当前索引位置数据,并用数组的最后一个数据代替之        site--;                         // 索引位置的上限减一(弃置最后一个数据)    }    return array;}

注意事项:

1.生成上w条不重复随机数据时,建议使用该方法;
2.该方法生成的不重复随机数,参数length 和 maxValue 没有相互的限制关系,适合生成类似随机考题的类似情况;
3.效率是三个方法中最高的,推荐使用;

三个方法的效率比较:
  maxValue是length的1000倍进行测试
这里写图片描述


完整代码:

using System;using System.Collections;using System.Diagnostics;namespace CSharp生成不重复的随机数{    class Program    {        /// <summary>        /// 利用Hashtable        /// </summary>        /// <remarks>        /// 使用条件:        ///             ///     1.该方法 length 和 maxValue的相关性比较强,使用时应该予以着重考虑        ///             ///     2.方法不适合生成 maxValue == length 相等的数据,        ///       例如:生成有10道考题的不重复题目号(要求从1-10,随机生成且不重复)        ///       maxValue最好不等于length参数,        ///       当 maxValue - minValue = length 时,时间效率差一个数量级【主要由于for循环内的生成语句导致】        ///       实际测试生成1w个数 maxValue - minValue 是 length 的        ///         2倍      :生成时间(使用7倍时)翻倍        ///         7倍及以上:生成时间基本一致        ///     4.使用HashTable保存 key 和 value 时,不要将同一个数值存入key和value,否则将导致数据顺序输出给数组        ///  注意:        ///       生成时间和具体使用的PC几性能有关,仅做参考!        /// </remarks>        static int[] UseHashTableToNonRepeatedRandom(int length, int minValue, int maxValue)        {            Hashtable hashtable = new Hashtable();            int seed = Guid.NewGuid().GetHashCode();            Random random = new Random(seed);            for (int i = 0; hashtable.Count < length; i++)            {                int nValue = random.Next(minValue, maxValue);                if (!hashtable.ContainsValue(nValue) && nValue != 0)                {                    hashtable.Add(i, nValue);                    //hashtable.Add(nValue, nValue);        // 将 key 和 value设置成一样的值,导致hashtable无法按添加顺序输出数组                    //Console.WriteLine(nValue.ToString());                }            }            int[] array = new int[hashtable.Count];            hashtable.Values.CopyTo(array, 0);            return array;        }        /// <summary>        /// 检查生成的随机数据是否重复        /// 如果重复则继续递归调用        /// 如果不重复则返回该数        /// </summary>        /// <param name="arr">生成的非重复随机数数组</param>        /// <param name="temp">随机数</param>        /// <param name="minValue">最大值</param>        /// <param name="maxValue">最小值</param>        /// <param name="random">生成随机数对象</param>        /// <returns>返回一个不重复的随机数</returns>        static int getNumberNonRepeatedRandom(int[] arr, int temp, int minValue, int maxValue, Random random)        {            int n = 0;            while (n <= arr.Length - 1)            {                if (arr[n] == temp) //利用循环判断是否有重复                 {                    temp = random.Next(minValue, maxValue); //重新随机获取。                     getNumberNonRepeatedRandom(arr, temp, minValue, maxValue, random);//递归:如果取出来的数字和已取得的数字有重复就重新随机获取。                 }                n++;            }            return temp;        }        /// <summary>        /// 递归,用它来检测生成的随机数是否有重复,如果取出来的数字和已取得的数字有重复就重新随机获取。        /// </summary>        /// <remarks>        /// 使用条件:        ///     1.maxValue - minValue = length。        ///       若差值过于接近或等于length将导致 Stack Overflow Excepion 异常        ///       该异常一般是因为函数调用栈溢出,         ///       也就是函数调用的层次超出了程序能够接受的范围.         ///       这种情况通常是由函数循环调用导致无限递归而引起的        ///     2.实际测试生成1w个数 maxValue  是 length 的        ///         2倍及以上:生成时间基本一致        /// </remarks>        static int[] RecursiveMethodToNonRepeatedRandom(int length, int minValue, int maxValue)        {            int seed = Guid.NewGuid().GetHashCode();            Random random = new Random(seed);            int[] array = new int[length];            int temp = 0;            for (int i = 0; i < length; i++)            {                temp = random.Next(minValue, maxValue); // 随机取数                 array[i] = getNumberNonRepeatedRandom(array, temp, minValue, maxValue, random); // 取出值赋到数组中             }            return array;        }        /// <summary>        /// 方法一 使用随机抽取数组index中的数,填充在新的数组array中,使数组array中的数是随机的        /// 方法一思路:用一个数组来保存索引号,先随机生成一个数组位置,然后把随机抽取到的位置的索引号取出来,        ///             并把最后一个索引号复制到当前的数组位置,然后使随机数的上限减一,具体如:先把这100个数放在一个数组内,        ///             每次随机取一个位置(第一次是1-100,第二次是1-99,...),将该位置的数用最后的数代替。        /// </summary>        /// <remarks>        /// 使用条件:        ///         1.生成上w条不重复随机数据时,建议使用该方法;        ///         2.该方法生成的不重复随机数,参数length 和 maxValue 没有相互的限制关系        /// </remarks>        static int[] UseDoubleArrayToNonRepeatedRandom(int length, int minValue, int maxValue)        {            int seed = Guid.NewGuid().GetHashCode();            Random radom = new Random(seed);            int[] index = new int[length];            for (int i = 0; i < length; i++)            {                index[i] = i + 1;            }            int[] array = new int[length]; // 用来保存随机生成的不重复的数             int site = length;             // 设置上限             int idx;                       // 获取index数组中索引为idx位置的数据,赋给结果数组array的j索引位置            for (int j = 0; j < length; j++)            {                idx = radom.Next(0, site - 1);  // 生成随机索引数                array[j] = index[idx];          // 在随机索引位置取出一个数,保存到结果数组                 index[idx] = index[site - 1];   // 作废当前索引位置数据,并用数组的最后一个数据代替之                site--;                         // 索引位置的上限减一(弃置最后一个数据)            }            return array;        }        static void Print(int[] array)        {            for (int i = 0; i < array.Length; i++)                Console.Write(string.Format("{0} ", array[i]));            Console.WriteLine();        }        static void Main(string[] args)        {            int arrayNum = 10000;            int minValue = 1;            int maxValue = arrayNum * 100;            Stopwatch sw = new Stopwatch();            sw.Start();            int[] array1 = UseHashTableToNonRepeatedRandom(arrayNum, minValue, maxValue + 1);            //Print(array1);            sw.Stop();            TimeSpan ts = sw.Elapsed;            Console.WriteLine("使用HashTable总共花费{0}ms.", ts.TotalMilliseconds);            sw.Reset();            sw.Start();            int[] array2 = RecursiveMethodToNonRepeatedRandom(arrayNum, minValue, maxValue + 1);            //Print(array2);            sw.Stop();            ts = sw.Elapsed;            Console.WriteLine("使用Recursion总共花费{0}ms.", ts.TotalMilliseconds);            sw.Reset();            sw.Start();            int[] array = UseDoubleArrayToNonRepeatedRandom(arrayNum, minValue, maxValue);            //Print(array);            sw.Stop();            ts = sw.Elapsed;            Console.WriteLine("使用DoubleArray创建不重复随机数总共花费{0}ms.", ts.TotalMilliseconds);            sw.Reset();            Console.ReadLine();        }    }}

参考链接:
C# Random 生成不重复随机数
c# Random太快产生的随机数会重复
C# 产生真随机数(RNGCryptoServiceProvider)
C#生成随机数的三种方法

0 0
原创粉丝点击