在编程中经常遇到一些类似的问题,比如做一个双色球选号软件,其中6个双色球是从1到33之间选出6个数来,这6个数是不能重复的,这个问题就是我们今天要说的生成不重复数算法。
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·算法描述如下:从M个数中选出N个数来(0<N<=M),要求N个数之间不能有重复。
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·这个问题我以前用J2SE实现过,使用了ArrayList,每次随机在指定范围内选定一个数,然后查看结果集合中是否存在该数,如果存在继续下一轮循环,如果不存在,就将该数保存到结果集合中去。使用这种算法虽然也能实现要求,缺点是判断结果集合中是否存在该数时,需要通过一个循环来判断,这会增加算法运行的时间,虽然时间复杂度为n,但多次重复,还是一笔不小的开销。
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·下面要介绍的算法是,每次随机取出一个数,之后将该数放置到集合的末尾去,这样下次取随机数的时候,只从1到目标集合个数-1个中随机抽取,如此循环,这样就避免了判断在结果集合中判断是否存在相冲突的数的过程。
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·算法代码如下:
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·using System;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
using System.Collections.Generic;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
using System.Text;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
using System.Diagnostics;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
using System.Management;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
namespace ConsoleApplications<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
class Programs<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
static void Main(string[] args)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
int[] range = new int[33];s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
for (int i = 0; i < 33; i++)//初始化范围集合,从1到33s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
range= i + 1;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
int[] result = CreateNumbers(range, 6);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
for(int i=0;i<result.Length;i++)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Console.WriteLine("result[{0}]={1}", i, result);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Console.ReadKey();s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
//取出不重复的6个数s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
static int[] CreateNumbers(int[] range, int count)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
int[] result = new int[count];s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Random random=new Random();s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
int index = 0;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
int temp = 0;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
for (int i = 0; i < count; i++)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
index=random.Next() % (range.Length-i);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
result = range[index];s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
//将当前已使用过的数移至集合末尾,并且将末尾原来没有使用的数放到当前位置s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
temp = range[range.Length - 1-i];s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
range[range.Length - 1-i] = range[index];s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
range[index]=temp;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
return result;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·结果如下:
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·补充一下,另外一种不使用数组而使用可变集合的办法,这种算法的做法是使用了之后马上从源集合中清除掉(数组是
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·没有办法这么做的),因而也是可以做到生成不重复的随机数的。具体代码如下:
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·//取出不重复的6个数s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
static List<int> CreateNumbers(List<int> range, int count)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
List<int> result = new List<int>(count);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Random random = new Random();s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
int index=0;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
for (int i = 0; i < count; i++)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
index=random.Next()%range.Count;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
result.Add(range[index]);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
range.RemoveAt(index);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
return result;s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·至于用法,也很简单:
s<¦Þã}æwww.netcsharp.cnmåÑþ{G · List<int> range = new List<int>(33);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
for (int i = 0; i < 33; i++)//初始化范围集合,从1到33s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
range.Add(i + 1);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
List<int> result = CreateNumbers(range, 6);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
for (int i = 0; i < result.Count; i++)s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
{s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
Console.WriteLine("result[{0}]={1}", i, result);s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·
}s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·经万次测试,使用数组方法性能比使用List<int>范型集合高1/4。
s<¦Þã}æwww.netcsharp.cnmåÑþ{G ·