您的位置:首页 > 编程语言 > C#

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

2017-03-02 21:28 681 查看

用 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
== 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
== 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#生成随机数的三种方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  不重复随机数