说明:写作本文的出发点是最近和一个有3年开发经验的.NET开发人员聊天,他跟我说经常没有思路,在实际开发中我也见过一个具有4、5年开发经验的开发人员几乎没有灵活变通的能力,所以打算写一系列文章,在这个系列文章中我会主要讲解解题的思路,而不是讲述什么新技术新特性,借这个系列文章为初中级开发者了解遇到问题别人是如何思考和解决的。当然,如果你的思路比本文提到的更好,欢迎指出来,同时如果你对本系列文章有更好的建议或者有日常中的一些典型问题,请给我联系,我们共同探讨。目前我暂时能想到的有不重复随机数产生问题、字符串与数值转换的问题、特殊的数据库锁问题、访客来路追踪问题、在线用户统计问题、统计用户访问页面偏好问题。
好了,我现在开始本篇的讲述。本篇的最原始形态是来源于我早年做的一个Java SE应用软件,它是用来模拟彩票投注站的选好软件的。应为在早年Java SE中用swing做界面布局是一件比较痛苦的事情,所以后来我重新用C#做了一个。这个问题的原型就是解决双色球随机选号的问题,我们知道双色球红色球共包含1到33这33个红色号码球及1到16这16个蓝色号码球,一注双色球号码应包括6个红色球号码和1个蓝色球号码。蓝色号码球很好解决,随机从1到16这16个数字中随机选取一个就行了。但是红色球就存在这样一样问题,每次选取的红色球不能与本注中已经选取的号码重复,这个问题归结为生成不重复随机数问题。
在本篇我就怎么生成不重复的红色球展开讨论。
解题思路一
在早期的Java中不包含泛型,只能使用ArrayList,所以我是用ArrayList来实现的。在Java中的ArrayList和C#中的ArrayList在用法上是很相似的(这就是为什么高手经常说掌握一门语言之后再去掌握另一门语言是很容易的事情,应为思想是相通的,呵呵)。在这里我最想想到的就是使用循环,每次循环中随机生成一个随机数,判断一下这个随机数是否已经在本注中使用,如果没有使用就将这个号码保存到结果中去,反之则进行下一轮循环,循环的结束条件就是生成了满足要求的6个数字。接触到泛型之后,我知道在这里我所使用的数据类型是int类型(当然也可以使用byte类型),如果使用ArrayList保存int这样的值类型数据会存在着装箱和拆箱操作,带来不必要的性能损失,所以针对这种集合中数据类型单一的情况可以考虑泛型集合,于是得到了下面的代码:
/// <summary> /// 从1到33中任意选取不重复的6个随机数 /// </summary> /// <returns></returns> public List<int> GenerateNumber1() { //用于保存返回的结果 List<int> result = new List<int>(6); Random random = new Random(); int temp = 0; //如果返回的结果集合中实际的元素个数小于6个 while (result.Count < 6) { //在[1,34)区间任意取一个随机整数 temp = random.Next(1, 34); if (!result.Contains(temp)) { //如果在结果集合中不存在这个数,则添加这个数 result.Add(temp); } } //result.Sort();//对返回结果进行排序 return result; |
当然,上面这种思路是可以实现的,但是每次随机生成一个随机数都要判断在结果集合中是否已经存在这个数,如果存在还要继续下一个循环,这样一来并不是每一轮循环都能生成一个有效(即不重复)的随机数,并且result.Contains(temp)尽管看起来只有一句,但实际在内部还是要通过循环来判断,效率还是较低。假如有一天有个人看到这篇文章,他想:很好,我终于可以试试了,我要从1到10000个数中取出9999个不重复的随机数,用上面的这个方法,可能很长时间都得不到结果(不要以为没有这样的人,我就遇见多多次不会变通的人,实际上最好的解决办法就是从10000中随机去掉一个就可以了,而不是照搬上面的套路)。