也谈生成不重复数的算法
刚刚看到有人写生成不重复的随机数的三种方法,正好在几年前我也有过类似的要求,实现类似于双色球随机选号的功能,也就是在M个数中选取N个不重复的数(M>N>0),下面我也谈谈我曾经用过的几种做法,也算是对作者提出的方法的一种补充吧。
一、利用Contains()判断法
思路是每次在制定范围中随机生成一个随机数,然后判断在已经生成的集合中是否存在,如果不存在就添加到结果集合中去,代码如下:
{
List<int> result = new List<int>(Count);
Random random = new Random();
int temp = 0;
//判断是否已经生成了足够的随机数
while (result.Count < Count)
{
temp = random.Next(Min, Max) + 1;
//判断在集合中是否存在
if (!result.Contains(temp))
{
result.Add(temp);
}
}
return result;
}
这种做法的缺点是效率比较低,因为不是每一次循环生成的随机数都是有效地,特别是当M和N接近时,这种算法的缺点极其明显。
二、删除法
这种做法的实现过程时实现将所有可能的值填充到一个容器集合中,每次随机从这个容器集合中取一个值出来放到结果集合中,并且从容器集合中删除这个已经用过的值,再进行下一次循环,从容器集合中取一个随机值,代码如下:
{
List<int> container = new List<int>(Max);
List<int> result = new List<int>(Count);
int index = 0;
Random random = new Random();
for (int i = Min; i <= Max; i++)
{
container.Add(i);
}
for (int i = 1; i <= Count; i++)
{
index = random.Next(container.Count);
result.Add(container[index]);
container.RemoveAt(index);
}
//result.Sort();
return result;
}
这种做法的优点是每次循环都能生成一个“有效”的随机数(指不会与现有的重复),缺点是需要先初始化所有可能的随机数容器集合。
三、调换位置法
这种算法的特点是使用数组,先初始化一个包含所有可能值的容器集合,然后再初始化一个存放结果的数组,每次从容器集合中随机取一个数出来用结果数组存放,然后在容器集合中调整钢材使用的那个数的位置,使它放在不适用的范围中(即标识为不再使用),下次再从容器集合中的所有标识为可用的数中随机取一个出来,代码如下:
{
int[] container = new int[33];
int[] result = new int[6];
Random random = new Random();
int index = 0;
int temp = 0;
for (int i = Min; i <= Max; i++)
{
container[i - 1] = i;
}
for (int i = 0; i < Count; i++)
{
index = random.Next(container.Length - i);
result[i] = container[index];
temp = container[container.Length - 1 - i];
container[container.Length - 1 - i] = container[index];
container[index] = temp;
}
//Array.Sort(result);
return result;
}
这种做法的优点也是不用每次判断选择的数是否在结果中是否存在,并且使用的数组,原理适用于不支持泛型集合的语言(如PHP等),缺点也是要先初始化所有可能值的容器集合。
总结,对于我个人来说,我比较倾向于后两种方法,因为不用去判断随机选择的数是否已经在结果集合中存在的情况,缺点也是这个,它需要在每次初始化容器集合(不过这个缺点也是它的优点,这种情况也可以应用于在指定范围的所有可能类型的集合中随机选择几个不重复的场合)。
最后,因为因为有其它事情,所以表达方式可能不是很清楚,请大家看看代码。
下面提供一个可运行的版本,代码如下:
using System.Collections;
using System.Collections.Generic;
namespace XMLApplication
{
class Program
{
private const int Min = 1;//随机数的取值范围的最小值
private const int Max = 33;//随机数的取值范围的最大值
private const int Count = 6;//要取的随机数的个数
static void Main(string[] args)
{
//again:
List<int> result1 = GenerateRandomNumber1();
ShowResult(result1);
List<int> result2 = GenerateRandomNumber2();
ShowResult(result2);
int[] result3 = GenerateRandomNumber3();
ShowResult(result3);
//Console.WriteLine("Repeat?Y|N");
//string key = Console.ReadLine();
//if (key.ToUpper().Equals("Y"))
//{
// goto again;
//}
}
public static List<int> GenerateRandomNumber1()
{
List<int> result = new List<int>(Count);
Random random = new Random();
int temp = 0;
//判断是否已经生成了足够的随机数
while (result.Count < Count)
{
temp = random.Next(Min, Max) + 1;
//判断在集合中是否存在
if (!result.Contains(temp))
{
result.Add(temp);
}
}
result.Sort();
return result;
}
public static List<int> GenerateRandomNumber2()
{
List<int> container = new List<int>(Max);
List<int> result = new List<int>(Count);
int index = 0;
Random random = new Random();
for (int i = Min; i <= Max; i++)
{
container.Add(i);
}
for (int i = 1; i <= Count; i++)
{
index = random.Next(container.Count);
result.Add(container[index]);
container.RemoveAt(index);
}
//result.Sort();
return result;
}
public static int[] GenerateRandomNumber3()
{
int[] container = new int[33];
int[] result = new int[6];
Random random = new Random();
int index = 0;
int temp = 0;
for (int i = Min; i <= Max; i++)
{
container[i - 1] = i;
}
for (int i = 0; i < Count; i++)
{
index = random.Next(container.Length - i);
result[i] = container[index];
temp = container[container.Length - 1 - i];
container[container.Length - 1 - i] = container[index];
container[index] = temp;
}
Array.Sort(result);
return result;
}
private static void ShowResult(IList<int> list)
{
Console.Write("[");
for (int i = 0; i < list.Count - 1; i++)
{
Console.Write(list[i] + ",");
}
Console.WriteLine(list[list.Count - 1] + "]");
}
}
}