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

用 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#生成随机数的三种方法

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值