算法一:
用一个List保存所有候选的整数,依次 生成一个随机索引(该索引的范围需要递减),返回该元素,并移除该索引对应的元素值。
(缺点 :该算法经测试大概1M 的整数范围需要用到约16M 的内存空间,所以对于 大数据范围,多线程且每个线程分别独立生成不重复的随机整数 的情况下不太适用。 优点 :该算法的可读性最强;而且因为会移除返回的元素,所以占用的内存会动态逐渐减少。)
所以此处没有给出代码示例。
算法二:
用一个数组保存所有候选的整数,依次 生成一个随机索引(该索引的范围需要递减),返回该元素,并 将末尾元素赋值到索引位置 且将原末尾元素位置前一个元素作为新的末尾元素。
(缺点 :可读性比上面的算法弱一点;占用内存一直不变。 优点 :该算法经测试大概 1M 的整数范围需要用到约 4M 的内存空间(因为一个int数据占用4个Byte),比上面的算法好一点;时间复杂度比较低。)
代码示例:
package com.kangfuqiang.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <pre>
* Title:不重复的随机整数类
* Description: 用于依次获取 [MIN,MAX] 范围内的不重复随机整数;
* 使用说明:需要new一个该类的实例(传入min和max参数),然后 使用该实例 依次获得不重复的随机整数;
*
* ps:1M 的整数范围时,该实例占用的相关内存约为 4M 。
*
* </pre>
*/
public class NoRepeatRandomInt {
private static Logger log = LoggerFactory.getLogger(NoRepeatRandomInt.class);
/** 最小值(包含) */
public final int MIN;
/** 最大值(包含) */
public final int MAX;
/** 计数器,记录已经领取不重复的随机整数的次数 */
private int counter = 0;
/** 保存 [MIN,MAX] 范围内的所有整数*/
private int[] numArr ;
/**
* @param min
* @param max
* @exception IllegalArgumentException 如果 min > max,则抛出该异常
*/
public NoRepeatRandomInt(int min, int max) {
if(min>max){
String str = String.format("构造器参数错误,最小值不能大于最大值!min:%s,max:%s", min, max);
log.error(str);
throw new IllegalArgumentException( str );
}
this.MIN = min;
this.MAX = max;
numArr = new int[this.getOriginalCnt()];
for(int i=0; i<numArr.length; i++){
numArr[i] = i + this.MIN;
}
}
/**
* 领取 下一个不重复的随机整数
* @return
* @exception RuntimeException 如果 领取次数 超过 总个数范围,将抛出运行时异常
*/
public int next(){
if(this.getRemainCnt()<=0){
String str = String.format("领取次数 超过 总个数范围(%d个)!", this.getOriginalCnt());
log.error(str);
throw new RuntimeException(str);
}
int idx = (int)(Math.random() * this.getRemainCnt());
int ret = numArr[idx];
numArr[idx] = numArr[this.getRemainCnt()-1];
numArr[this.getRemainCnt()-1] = ret;
this.counter++;
return ret;
}
/** 获得原始个数 */
private int getOriginalCnt(){
return this.MAX - this.MIN + 1;
}
/** 获得剩余个数 */
private int getRemainCnt(){
return this.MAX - this.MIN + 1 - counter;
}
}
算法三:
使用Set集合来保证或保存不重复的随机整数。
这种算法适合 (返回数目/ 范围数目)的值非常小 的情况,
其他情况还是使用上面的两种算法比较好。
PS:
网上还有一些其他的算法,但是大多时间复杂度比较高,空间复杂度也没有明显的好处。
1、遍历 已返回的随机数 的集合,以保证不重复随机整数。
2、初始化List后,随机排序一下(shuffle),然后依次返回。
3、初始化数组后,数组随机调换位置,然后依次返回。
欢迎大家交流、指点、评论与回复