java实现一个抽奖概率类

在一些项目需求中,可能会遇到抽奖问题,如提供一系列奖品及获奖概率,要求根据概率返回每次抽到的奖品。以下是本人在实际项目中写的一个抽奖工具类,与大家共同分享:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 抽奖工具类,概率和可以不等于1
 * 概率为百分数去掉百分号的部分,如10%,则为10
 * 抽奖操作如下:
 * 1.输入抽奖概率集合,【抽奖概率集合为{10.0, 20.0, 30.0}】
 * 2.生成连续集合,       【生成的连续集合为{(0.0, 10.0],(10.0, 30.0],(30.0, 60.0]}】
 * 3.生成随机数,          【生成方法为 random.nextDouble() * maxElement】
 * 4.判断随机数在哪个区间内,返回该区间的index【生成了随机数12.001,则它属于(10.0, 30.0],返回 index = 1】
 * 
 */
public class LotteryUtil {
	
	/**
	 * 定义一个连续集合
	 * 集合中元素x满足:(minElement,maxElement]
	 * 数学表达式为:minElement < x <= maxElement
	 *
	 */
	public class ContinuousList {
		
		private double minElement;
		private double maxElement;
		
		public ContinuousList(double minElement, double maxElement){
			if(minElement > maxElement){
				throw new IllegalArgumentException("区间不合理,minElement不能大于maxElement!");
			}
			this.minElement = minElement;
			this.maxElement = maxElement;
		}
		
		/**
		 * 判断当前集合是否包含特定元素
		 * @param element
		 * @return
		 */
		public boolean isContainKey(double element){
			boolean flag = false;
			if(element > minElement && element <= maxElement){
				flag = true;
			}
			return flag;
		}
		
	}
	
	private List lotteryList;   //概率连续集合
	private double maxElement; 					//这里只需要最大值,最小值默认为0.0

	/**
	 * 构造抽奖集合
	 * @param list 为奖品的概率
	 */
	public LotteryUtil(List list){
		lotteryList = new ArrayList();
		if(list.size() == 0){
			throw new IllegalArgumentException("抽奖集合不能为空!");
		}
		double minElement = 0d;
		ContinuousList continuousList = null;
		for(Double d : list){
			minElement = maxElement;
			maxElement = maxElement + d;
			continuousList = new ContinuousList(minElement, maxElement);
			lotteryList.add(continuousList);
		}
	}
	
	/**
	 * 进行抽奖操作
	 * 返回:奖品的概率list集合中的下标
	 */
	public int randomColunmIndex(){
		int index = -1;
		Random r = new Random();
		double d = r.nextDouble() * maxElement;  //生成0-1间的随机数
		if(d == 0d){
			d = r.nextDouble() * maxElement;     //防止生成0.0
		}
		int size = lotteryList.size();
		for(int i = 0; i < size; i++){
			ContinuousList cl = lotteryList.get(i);
			if(cl.isContainKey(d)){
				index = i;
				break;
			}
		}
		if(index == -1){
			throw new IllegalArgumentException("概率集合设置不合理!");
		}
		return index;
		
	}
	
	public double getMaxElement() {
		return maxElement;
	}

	public List getLotteryList() {
		return lotteryList;
	}
	public void setLotteryList(List lotteryList) {
		this.lotteryList = lotteryList;
	}

	
}
该工具类的基本思想是,将抽奖概率分布到数轴上,如现有三个抽奖概率10、20、30,将三者依次添加到概率集合中,则构造的数轴为:0~10范围内表示概率10,10~30范围内表示概率为20,30~60范围内表示概率为30,数轴上的长度对应着相应的概率。由这种处理方式可知,概率总和并不需要等于1。该工具类的成功与否在于Random.nextDouble()能否等概率地生成0~1之间的任意一个数。

对该抽奖工具进行测试,测试类如下:

package com.lottery;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

class Result{
	private int index;
	private int sumTime;
	private int time;
	private double probability;
	private double realProbability;
	
	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public int getTime() {
		return time;
	}

	public void setTime(int time) {
		this.time = time;
	}

	public int getSumTime() {
		return sumTime;
	}

	public void setSumTime(int sumTime) {
		this.sumTime = sumTime;
	}

	public double getProbability() {
		return probability;
	}

	public double getRealProbability() {
		return realProbability;
	}

	public void setRealProbability(double realProbability) {
		this.realProbability = realProbability;
	}

	public Result(){
		
	}
	
	public Result(int index, int sumTime, int time, double realProbability) {
		this.setIndex(index);
		this.setTime(time);
		this.setSumTime(sumTime);
		this.setRealProbability(realProbability);
		
	}

	public String toString(){
		return "索引值:" + index + ",抽奖总数:" + sumTime + ",抽中次数:" + time + ",概率:" 
	           + realProbability + ",实际概率:" + (double)time/sumTime;
	}
}

public class TestLottery {
	
	static final int TIME = 100000;
	
	public static void iteratorMap(Map map, List list){
		for(Entry entry : map.entrySet()){
			int index = entry.getKey();
			int time  = entry.getValue();
			Result result = new Result(index, TIME, time, list.get(index));
			System.out.println(result);
		}
	}
	
	public static void main(String[] args) {
		//构造概率集合
		List list = new ArrayList();
		list.add(20d);
		list.add(80d);
		list.add(50d);
		list.add(30d);
		LotteryUtil ll = new LotteryUtil(list);
		double sumProbability = ll.getMaxElement();
		
		Map map = new HashMap();
		for(int i = 0; i < TIME; i++){
			int index = ll.randomColunmIndex();
			if(map.containsKey(index)){
				map.put(index, map.get(index) + 1);
			}else{
				map.put(index, 1);
			}
		}
		for(int i = 0; i < list.size(); i++){
			double probability = list.get(i) / sumProbability;
			list.set(i, probability);
		}
		iteratorMap(map, list);
		
	}
	
	
	
}

运行结果:


由结果可知,抽奖100000时, 得到的实际概率基本与正式概率相当。

以下说明此类调用方式:

public LotteryUtil(List list)
说明:构造方法,传入参数为一个概率集合
public int randomColunmIndex()
功能:进行抽奖操作,返回List集合的索引下标,此下标对应的概率的奖品即为抽中的奖品




你可能感兴趣的:(java)