java 抽奖_java抽奖工具类:按概率抽奖

在一些抽奖活动中,会有按概率抽取奖品的操作,本文将提供一个抽奖概率的解决方案。

假设抽奖集合如下:

奖品id

概率

1

10%

2

10%

3

20%

4

20%

生成集合 生成的连续集合为{(0.0, 10.0],(10.0, 30.0],(30.0, 60.0]},如下:

奖品id

区间

1

(0.0, 10.0]

2

(10.0, 20.0]

3

(20.0, 40.0]

4

(40.0, 60.0]

最大的概率值 maxElement = 60.0,这个值下面会用到。

生成随机数

生成方法为 random.nextDouble() * maxElement,maxElement 就是上一步得到的。

判断随机数在哪个区间内,返回该区间对应的奖品id

假如 生成了随机数12.001,则它属于(10.0, 20.0],对应的奖品id为2.

代码如下:

LotteryUtil

public class LotteryUtil {

private List lotteryList;

private double maxElement;

/**

* 得到一个抽奖对象

* @param map 抽奖概率,key:奖品id,value:概率

* @param

* @return

*/

public static LotteryUtil getInstance(Map map) {

return new LotteryUtil(map);

}

/**

* 构造抽奖集合

* @param map 为奖品的概率

*/

private LotteryUtil(Map map) {

if(map.size() == 0){

throw new IllegalArgumentException("集合不能为空!");

}

double minElement = 0d;

lotteryList = new ArrayList<>(map.size());

for(Map.Entry entry : map.entrySet()) {

if(null == entry.getValue() || entry.getValue() <= 0) {

continue;

}

minElement = maxElement;

maxElement = maxElement + entry.getValue();

Continuous continuous = new Continuous(entry.getKey(), minElement, maxElement);

lotteryList.add(continuous);

}

}

/**

* 抽奖操作

*

* @return

*/

public T lottery() {

Random r = new Random();

//生成随机数

double d = r.nextDouble() * maxElement;

if(d == 0d) {

// 如果生成了0,就再生成一次

d = r.nextDouble() * maxElement;

}

int size = lotteryList.size();

for(int i = 0; i < size; i++){

Continuous continuous = lotteryList.get(i);

if(continuous.isContainKey(d)){

return (T)continuous.getVal();

}

}

return null;

}

/**

* 定义一个连续区间

* 集合中元素x满足:(minElement, maxElement]

* 数学表达式为:minElement < x <= maxElement

*

*/

class Continuous {

private Object val;

private double minElement;

private double maxElement;

public Continuous(Object val, double minElement, double maxElement) {

this.val = val;

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;

}

public Object getVal() {

return val;

}

public void setVal(Object val) {

this.val = val;

}

public double getMinElement() {

return minElement;

}

public void setMinElement(double minElement) {

this.minElement = minElement;

}

public double getMaxElement() {

return maxElement;

}

public void setMaxElement(double maxElement) {

this.maxElement = maxElement;

}

}

}

测试类:

LotteryUtilTest

public class LotteryUtilTest {

@Test

public void testLottery() {

Map map = new HashMap<>();

map.put(1, 0.10d);

map.put(2, 0.10d);

map.put(3, 0.20d);

map.put(4, 0.20d);

Map retMap = new HashMap<>();

int times = 100000;

LotteryUtil lotteryUtil = LotteryUtil.getInstance(map);

for(int i = 0; i < times; i++) {

Integer result = lotteryUtil.lottery();

Integer num = retMap.get(result);

retMap.put(result, null == num ? 1 : num + 1);

}

System.out.println(retMap);

Map setRate = new HashMap<>();

double total = 0d;

for(Map.Entry entry : map.entrySet()) {

total += entry.getValue();

}

for(Map.Entry entry : map.entrySet()) {

setRate.put(entry.getKey(), entry.getValue() / total);

}

Map realRate = new HashMap<>();

for(Map.Entry entry : retMap.entrySet()) {

realRate.put(entry.getKey(), (double)(entry.getValue()) / times);

}

for(Map.Entry entry : realRate.entrySet()) {

Integer key = entry.getKey();

System.out.println(String.format("奖品id为:%s, 抽奖次数为:%s, 设定的概率为:%s,实际的概率为:%s",

key, times, setRate.get(key), realRate.get(key)));

}

}

}

运行结果:

{1=16611, 2=16565, 3=33412, 4=33412}

奖品id为:1, 抽奖次数为:100000, 设定的概率为:0.16666666666666666,实际的概率为:0.16611

奖品id为:2, 抽奖次数为:100000, 设定的概率为:0.16666666666666666,实际的概率为:0.16565

奖品id为:3, 抽奖次数为:100000, 设定的概率为:0.3333333333333333,实际的概率为:0.33412

奖品id为:4, 抽奖次数为:100000, 设定的概率为:0.3333333333333333,实际的概率为:0.33412

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值