一个简单的抽奖算法

通过随机数的区间分布实现一个抽奖算法。接受一个包含奖品中奖概率的list,返回中奖的奖品。
先定义一个抽象奖品类。
public abstract class AbstractPrize {

    /**
     * 奖品概率
     */
    private double probability;
    
    public double getProbability() {
      return probability;
    }

    public void setProbability(double probability) {
      this.probability = probability;
    }
}

抽奖算法,思路是通过奖品中奖概率的最大小数位数获得一个基数,比如奖品为a,b,c,d,e中奖概率分别为0.3,0.050,0.1,0.3,0.25,那么得到基数为1000,奖品分布区间为0<=a<300,300<b<350,350<=c<450,450<=d<750,750<=e<1000,随后获得一个0-1000的随机数,顺序遍历奖品(很重要,不能多线程遍历),<300返回a,<350返回b,<450返回c,<750返回d,<1000返回e.

/**
 * 抽奖算法
 * @param prizes
 * @param <T>
 * @return
 * @throws IllegalArgumentException
 */
public static <T> T lotteryFunction(List<? extends AbstractPrize> prizes) throws IllegalArgumentException{
prizes = prizes.stream().filter(e -> e.getProbability() != 0d).collect(Collectors.toList());
Double base = this.verification(prizes);
int index = 0;
if(base != 0d) {
    BigDecimal fixRate = new BigDecimal("0");
    //产生随机数
    Random random = new Random();
    long result = nextLong(random, base.longValue());
    BigDecimal res = new BigDecimal(String.valueOf(result));
    //计算奖品分布区间
    for (AbstractPrize prize : prizes) {
        if(prize.getProbability() == 0d){
            continue;
        }
        fixRate = fixRate.add(new BigDecimal(String.valueOf(prize.getProbability()))
            .multiply(new BigDecimal(String.valueOf(base))));
        Boolean flag = res.compareTo(fixRate) == -1;
        if (flag) {
            break;
        }	
        index++;
}  
if(index <= prizes.size() - 1 ) {
            return (T) prizes.get(index);
        }
    }
    return null;
}
 
 
/**
 * 产生一个指定范围内的long型随机数
 * @param rng
 * @param n
 * @return
 */
private static long nextLong(Random rng, long n) {
    long bits, val;
    do {
        bits = (rng.nextLong() << 1) >>> 1;
        val = bits % n;
    } while (bits-val+(n-1) < 0L);
    return val;
}

/**
 * 计算基数精度,校验所有概率和不能超过1
 * @param prizes
 * @return
 * @throws IllegalArgumentException
 */
private static Double verification(List<? extends AbstractPrize> prizes) throws IllegalArgumentException{
    BigDecimal rateSum = new BigDecimal("0");
    Double base = 0d;
    for(AbstractPrize prize : prizes ) {
        rateSum = rateSum.add(new BigDecimal(String.valueOf(prize.getProbability())));
        Boolean verify = prize.getProbability() < 0 || rateSum.compareTo(new BigDecimal("1")) == 1;
        if(verify){
            throw new IllegalArgumentException("probability error");
        }
        String str = prize.getProbability() + "";
        int len = str.length() - str.indexOf(".") - 1;
        //最小基数为100,好像没必要
        Double temp = Math.pow(10,len < 2 ? 2 : len);
        if(temp > base){
            base = temp;
        }
    }
    return base;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值