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<ContinuousList> lotteryList //概率连续集合
private double maxElement //这里只需要最大值,最小值默认为0.0
/**
* 构造抽奖集合
* @param list 为奖品的概率
*/
public LotteryUtil(List<Double> list){
lotteryList = new ArrayList<ContinuousList>()
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<ContinuousList> getLotteryList() {
return lotteryList
}
public void setLotteryList(List<ContinuousList> lotteryList) {
this.lotteryList = lotteryList
}
}
该工具类的基本思想是,将抽奖概率分布到数轴上,如现有三个抽奖概率10、20、30,将三者依次添加到概率集合中,则构造的数轴为:0~10范围内表示概率10,10~30范围内表示概率为20,30~60范围内表示概率为30,数轴上的长度对应着相应的概率。由这种处理方式可知,概率总和并不需要等于1。该工具类的成功与否在于Random.nextDouble()能否等概率地生成0~1之间的任意一个数。
对该抽奖工具进行测试,测试类如下:
[java] view plain copy
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<Integer, Integer> map, List<Double> list){
for(Entry<Integer, Integer> 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<Double> list = new ArrayList<Double>()
list.add(20d)
list.add(80d)
list.add(50d)
list.add(30d)
LotteryUtil ll = new LotteryUtil(list)
double sumProbability = ll.getMaxElement()
Map<Integer, Integer> map = new HashMap<Integer, Integer>()
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时, 得到的实际概率基本与正式概率相当。
以下说明此类调用方式:
[java] view plain copy
public LotteryUtil(List<Double> list)
说明:构造方法,传入参数为一个概率集合
[java] view plain copy
public int randomColunmIndex()
功能:进行抽奖 *** 作,返回List集合的索引下标,此下标对应的概率的奖品即为抽中的奖品
红包剩余金额为 M红包剩余数量为 N
这种算法就是每次都在区间[0,M/N×2] 随机取一个数。假设100元红包发10个人,那么合理的做法应该是每个人领到10元的概率相同。
这样推导下去,每个人领到相同金额的概率应该就是相同的了。
第一次生成随机数: k1=(0,sum/n*2) (左开右开区间内的随机数)
第二次生成随机数:k2 = (0,(sum-k1)/(n-1)*2)
第三次生成随机数:k3 = (0,(sum-k1-k2)/(n-2)*2)
第N次生成随机数:kn = sum-k1-...-kn-1
这个算法可以把总金额想象成一条线段,每个人都有机会切一刀,前面的人切剩下的后面的人再接着切,这样越是前面的人截取的长度(理解成领取到的红包金额)越大的概率就越大。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)