一种范围随机数的实现

在兲朝游戏开发中,经常出现抽奖形式坑人的情况,这需要用到概率。然而java api所自带的是伪随机的接口,随机后几率并不均匀。这就出现了抽奖掉落概率未如理想的状况。为了使概率更均匀,将范围内全数字随机可以达到目的。当然,将全数字映射出来需要更高的内存消耗。更大的消耗是因为总想针对某个活动得到更均匀的概率,而不是整个框架都一个全数字数组来随机使用,因为这将使随机结构偏离原有的单个设定。


先来一种安全的简单全数字随机。将全数组字在数组之内打乱,然后通过不断缩小随机范围在数组之内随机后交换,确保在size次随机后,所有数字都被随机了一遍。


import java.util.Random;


public class ExactRandom {
/**
* 随机数字总数
*/
private final int size;
/**
* 全数字数组
*/
private final int[] arr;
/**
* 随机长度下限
*/
private int bound;
public Random r = new Random();


public ExactRandom(int size,int offset) {
super();
this.size = size;
arr = new int[size];
for(int i=0; i<size; i++){
arr[i] = i+offset;
}
for(int i=0; i<size; i++){
int pos = r.nextInt(size);
int temp = arr[i];
arr[i] = arr[pos];
arr[pos] = temp;
}
bound = size;
}

/**
* 更大的随机集群使用
* @param arr
*/
public ExactRandom(int[] arr) {
super();
this.arr = arr;
this.size = arr.length;
this.bound = size;
}


public synchronized int getRandonNum(){
int pos = r.nextInt(bound--);
int temp = arr[pos];
arr[pos] = arr[bound];
arr[bound] = temp;
if(bound <= 0){
bound = size;
}
return temp;
}


public int[] getArr() {
return arr;
}

}

为了保证在取随机数过程中数组内的数字交换不会引起竞争问题,使用了同步锁保证安全性。然而,在多用户多线程调用下很容易因锁竞争而使得效率低下。为此,将参照ConcurrentHashMap的分段锁思想,将随机数组切割成许多小块,以期将锁限制在自旋锁、轻量级锁的范围内,大幅减少重量级锁出现的几率。通过使用一个AtomicInteger,使在相邻两次的取随机数调用中命中不同的随机数组,减低同时命中同一ExactRandom所引起的锁升级。当lockSize数值越大时,锁竞争几率越小。


import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

public class HyperExactRandom {
	
	private final int size;
	private final int offset;
	private final int lockSize;
	private final ExactRandom[] rans;
	private AtomicInteger lockPos = new AtomicInteger();
	
	public HyperExactRandom(int size,int lockSize, int offset) {
		super();
		this.size = size;
		this.offset = offset;
		this.lockSize = lockSize;
		int patition = size/lockSize; 
		int lastPartition = patition + size%lockSize;
		rans = new ExactRandom[lockSize];
		
		ExactRandom temp = new ExactRandom(size, offset);
		int[] fullArr = temp.getArr(); temp = null;
		for(int i=0,lastPos = lockSize -1; i<lockSize; i++){
			int from = i*patition;
			int to   = i != lastPos ? from+patition : from+lastPartition; 
			rans[i] = new ExactRandom(Arrays.copyOfRange(fullArr, i*patition, to));
		}

	}
	
	
	public int getRandom(){
		int pos = lockPos.getAndIncrement();
		if(pos > 0){
			pos %= lockSize;
		}else if(pos < 0){
			long t = 0xFFFFFFFFL;
			t = t&pos % lockSize;
			pos = (int)t;
		}
		return rans[pos].getRandonNum();
	}


	public int getSize() {
		return size;
	}


	public int getOffset() {
		return offset;
	}
	
//	public static void main(String[] args) {
//		int fullSize = 1000000;
//		HyperExactRandom ran = new HyperExactRandom(fullSize, 1000, 1);
////		HashSet<Integer> set = new HashSet<>(2*fullSize);
//		long sum = 0;
//		for(int i=0; i<fullSize;i++){
//			sum += ran.getRandom();
//		}
//		
//		long sum1 = 0;
//		for(int i=0; i<10*fullSize;i++){
//			sum1 += ran.getRandom();
//			if(i % fullSize == 0 &&  i != 0){
//				System.out.println("rest ="+ (sum1%sum));
//			}
//		}
//		
//		System.out.println(sum);
//	}
	
	
	
	

}

滥用全数字随机并不是一个好想法,在简单的一次应用中,假设随机数字达到1百万,随机用的整形数组将消耗4MB内存,这似乎并不是好大的消耗。但当每个需要的随机的模块为了自身随机概率的均匀而都生成自己私有的全数字随机数组时,将会造成非常大的消耗。实际开发中何时使用需要斟酌。



你可能感兴趣的:(java,Random,游戏开发)