获取1-n之间不重复随机数的几种方法
获取1-n之间不重复随机数的几种方法
最近接到一个小任务,给定一个整数n,获取1-n之间不重复的n个随机数。于是我便动手写了写,发现实现不难,但是一般时间复杂度都比较高,于是思考了一下,又看了其他一些人的思路,最终总结了以下三种实现方式。
第一种:最常规的方式,flag标志位判重,这种方式是最容易想到的,也是时间复杂度很高的一种.
package com.wl.test3; import java.util.Scanner; /** * 给定一个整数n,生成1-n之间不同的n个随机整数 * Math.random()-->[0.0,1.0) * @author Administrator * */ public class TestRandom { public static void getRandom(int n) { /** 生成的n个随机数数组 */ int[] numbers = new int[n]; /** 随机数的个数 */ int count = 0; while (count < n) { /** 生成[1,n+1)之间的随机整数 */ int number = (int) (Math.random() * n + 1); /** 标志位,用于判断生成的随机整数是否已经存在相同的 */ boolean flag = true; /** 第count个整数与前边生成的count-1个数进行比较 */ for (int i = 0; i < count; i++) { /** 若存在相同的,标志位置false,跳出循环,重新生成一个整数 */ if (number == numbers[i]) { flag = false; break; } } if (flag) { numbers[count] = number; count++; } } for (int num : numbers) { System.out.println(num); } } @SuppressWarnings("resource") public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("请输入一个整数n:"); Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); long start =System.currentTimeMillis(); getRandom(n); long end =System.currentTimeMillis(); System.out.println("共耗时:"+(end-start)+"毫秒"); } }
第二种:使用set集合判重,运行时间上要比第一种快些,但是因为set底层实现本身时间复杂度就要o(n),所以使用起来也并不是很理想。
package com.wl.test3; import java.util.HashSet; import java.util.Scanner; import java.util.Set; public class TestRandom2 { public static void getRandom(int n,Set<Integer> set) { int size=0; while(size<n){ int number = (int) (Math.random() * n + 1); set.add(number); size=set.size(); } for(Integer num:set){ System.out.println(num); } } public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("请输入一个整数n:"); Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); Set<Integer> set=new HashSet<Integer>(); long start=System.currentTimeMillis(); getRandom(n,set); long end=System.currentTimeMillis(); System.out.println("运行时间:"+(end-start)+"毫秒"); } }
第三种:使用两个数组,一个存储有序的可能生成的所有的随机数数值,一个存放最终生成的随机数。生成的随机数不再直接取,而是作为数组索引,在数组生成的随机索引的位置取相应元素,这样也能保证取得元素是随机的,而且生成的随机数范围逐渐减小,可以避免出现重复值,这种是时间复杂度最低的一种。
package com.wl.test3; import java.util.Scanner; public class TestRandom11 { public static void dis(int n) { /**存放1-n元素的数组,便于随机选取*/ int[] b =new int[n]; /**存放生成的随机数的数组*/ int[] a=new int[n]; /**生成的随机数的上界,即获取数组索引的上界*/ int max=n; /**生成元素为1-n的数组*/ for(int i=0;i<n;i++) { b[i]=i+1; } for(int i=0;i<n;i++) { /**随机生成一个数,作为获取数组元素的索引,实际取得随机数还是数组b中的元素*/ int index=(int) (Math.random() * max ); /**随机数作为数组b的索引获取该索引位置上的元素,赋给数组a,保证a中的元素是随机获取的*/ a[i]=b[index]; /**将数组b最后一个元素赋给该索引位置的元素*/ b[index]=b[max-1]; /**max递减,避免获取数组b中的最后一个元素,出现重复的现象*/ max--; } for(int i=0;i<n;i++) { System.out.println(a[i]); } } public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("请输入一个整数n:"); Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); long start =System.currentTimeMillis(); dis(n); long end =System.currentTimeMillis(); System.out.println("共耗时:"+(end-start)+"毫秒"); } }
比较:
1.生成100个随机数,数量不大,因此三种方式的运行时间也差别不大,大概有在2,3毫秒左右。
2.生成1000个随机数,数量增加,也不是很大时,第一二种方式,差别不大,大概分别是19,22毫秒左右,而第三种则有明显提升,13毫秒左右。
3.生成10000个随机数,数量比较大时,差别很大,第一种耗时617毫秒,第二种耗时109毫秒,第三种则耗时64毫秒,此时第一种方式可以很明显的看出速度很慢。
总结一下,在生成大量的随机数时,还是推荐使用第三种方式。