不重复随机数列生成算法

数据库 算法
本文将讲述一个高效的不重复随机数列的生成算法,其效率比通常用hashtable消重的方法要快很多。

本文将讲述一个高效的不重复随机数列的生成算法,其效率比通常用hashtable 消重的方法要快很多。

首先我们来看命题:

给定一个正整数n,需要输出一个长度为n的数组,数组元素是随机数,范围为0 – n-1,且元素不能重复。比如 n = 3 时,需要获取一个长度为3的数组,元素范围为0-2,

比如 0,2,1。

这个问题的通常解决方案就是设计一个 hashtable ,然后循环获取随机数,再到 hashtable 中找,如果hashtable 中没有这个数,则输出。下面给出这种算法的代码

  1. public static int[] GetRandomSequence0(int total)  
  2. {  
  3.     int[] hashtable = new int[total];  
  4.     int[] output = new int[total];  
  5.  
  6.     Random random = new Random();  
  7.     for (int i = 0; i < total; i++)  
  8.     {  
  9.         int num = random.Next(0, total);  
  10.         while (hashtable[num] > 0)  
  11.         {  
  12.             num = random.Next(0, total);  
  13.         }  
  14.  
  15.         output[i] = num;  
  16.         hashtable[num] = 1;  
  17.     }  
  18.  
  19.     return output;  

代码很简单,从 0 到 total - 1 循环获取随机数,再去hashtable 中尝试匹配,如果这个数在hashtable中不存在,则输出,并把这个数在hashtable 中置1,否则循环尝试获取随机数,直到找到一个不在hashtable 中的数为止。这个算法的问题在于需要不断尝试获取随机数,在hashtable 接近满时,这个尝试失败的概率会越来越高。

那么有没有什么算法,不需要这样反复尝试吗?答案是肯定的。

 如上图所示,我们设计一个顺序的数组,假设n = 4

***轮,我们取 0 – 3 之间的随机数,假设为2,这时,我们把数组位置为2的数取出来输出,并把这个数从数组中删除,这时这个数组变成了

第二轮,我们再取 0-2 之间的随机数,假设为1,并把这个位置的数输出,同时把这个数从数组删除,以此类推,直到这个数组的长度为0。这时我们就可以得到一个随机的不重复的序列。

这个算法的好处是不需要用一个hashtable 来存储已获取的数字,不需要反复尝试。算法代码如下:

  1. public static int[] GetRandomSequence1(int total)  
  2. {  
  3.     List<int> input = new List<int>();  
  4.     for (int i = 0; i < total; i++)  
  5.     {  
  6.         input.Add(i);  
  7.     }  
  8.  
  9.     List<int> output = new List<int>();  
  10.  
  11.     Random random = new Random();  
  12.     int end = total;  
  13.     for (int i = 0; i < total; i++)  
  14.     {  
  15.         int num = random.Next(0, end);  
  16.         output.Add(input[num]);  
  17.         input.RemoveAt(num);  
  18.         end--;  
  19.     }  
  20.  
  21.     return output.ToArray();  
  22. }  

这个算法把两个循环改成了一个循环,算法复杂度大大降低了,按说速度应该比***个算法要快才对,然而现实往往超出我们的想象,当total = 100000 时,测试下来,***个算法用时 44ms, 第二个用时 1038 ms ,慢了很多!这是为什么呢?问题的关键就在这个 input.RemoveAt 上了,我们知道如果要删除一个数组元素,我们需要把这个数组元素后面的所有元素都向前移动1,这个移动操作是非常耗时的,这个算法慢就慢在这里。到这里,可能有人要说了,那我们不用数组,用链表,那删除不就很快了吗?没错,链表是能解决删除元素的效率问题,但查找的速度又大大降低了,无法像数组那样根据数组元素下标直接定位到元素。所以用链表也是不行的。到这里似乎我们已经走到了死胡同,难道我们只能用hashtable  反复尝试来做吗?在看下面内容之前,请各位读者先思考5分钟。

#p#

…… 思考5分钟

[[20734]] 

#p#

算法就像一层窗户纸,隔着窗户纸,你永远无法知道里面是什么,一旦捅穿,又觉得非常简单。

还是上面那个例子,假设 n = 4

***轮,我们随机获得2时,我们不将 2 从数组中移除,而是将数组的***一个元素移动到2的位置

 这时数组变成了

 第二轮我们对 0-2 取随机数,这时数组可用的***一个元素位置已经变成了2,而不是3。假设这时取到随机数为1

我们再把下标为2 的元素移动到下标1,这时数组变成了

以此类推,直到取出n个元素为止。

这个算法的优点是不需要用一个hashtable 来存储已获取的数字,不需要反复尝试,也不用像上一个算法那样删除数组元素,要做的只是每次把数组有效位置的***一个元素移动到当前位置就可以了,这样算法的复杂度就降低为 O(n) ,速度大大提高。

经测试,在 n= 100000 时,这个算法的用时仅为7ms。

下面给出这个算法的实现代码

  1. /// <summary>  
  2. /// Designed by eaglet  
  3. /// </summary>  
  4. /// <param name="total"></param>  
  5. /// <returns></returns>  
  6. public static int[] GetRandomSequence2(int total)  
  7. {  
  8.  
  9.     int[] sequence = new int[total];  
  10.     int[] output = new int[total];  
  11.  
  12.     for (int i = 0; i < total; i++)  
  13.     {  
  14.         sequence[i] = i;  
  15.     }  
  16.  
  17.     Random random = new Random();  
  18.  
  19.     int end = total - 1;  
  20.  
  21.     for (int i = 0; i < total; i++)  
  22.     {  
  23.         int num = random.Next(0, end + 1);  
  24.         output[i] = sequence[num];  
  25.         sequence[num] = sequence[end];  
  26.         end--;  
  27.     }  
  28.  
  29.     return output;  
  30. }  

原文链接: http://www.cnblogs.com/eaglet/archive/2011/01/17/1937083.html

【编辑推荐】

  1. 为自己做一个简单记账簿
  2. 一步一步设计你的数据库1
  3. 数据库入门级之算法【一】
  4. 数据库入门级之算法【二】
  5. 数据库入门级之算法【三】

 

责任编辑:艾婧 来源: 博客园
相关推荐

2009-06-11 15:16:18

不重复随机数Java

2017-05-29 09:56:25

2009-12-02 17:01:01

PHP随机数rand()

2021-11-07 14:33:48

算法Pairwise功能

2017-07-14 10:35:06

2017-01-03 15:16:56

Tofsee僵尸网络恶意软件

2019-12-26 14:07:19

随机数伪随机多线程

2021-01-21 11:04:42

Python 开发编程语言

2010-03-22 19:41:31

2010-03-11 12:48:25

Python生成随机数

2014-07-23 10:07:34

2022-12-15 08:54:28

JAVA性能JDK

2021-03-22 10:05:03

算法可视化大数据

2022-01-27 10:06:29

生成算法分布式

2012-02-16 08:27:14

安全漏洞RSA算法

2016-12-28 10:45:39

2021-04-06 08:54:13

Random线程安全数生成器

2019-09-11 10:09:00

Java虚拟机算法

2021-06-15 07:59:01

Java生成随机数Java编程

2021-12-27 09:31:20

HashtableJava随机数
点赞
收藏

51CTO技术栈公众号