zoukankan      html  css  js  c++  java
  • 实现一个简单的概率发奖问题

    WOW,好长时间没有更新博客了,最近加班,加到屎...果然年轻就要996...哈哈

    最近,TA们又有了个新的点子,也不对,可能只是在某处看到的点子,不过..who care!,反正最后我来搬砖,除了CRUD之外好像没啥新颖的地方,

    不过有个地方我还是学习了下,参考了一位博主写的:

    给定N个概率集合,从总随机抽取,发放奖品,限定前M名必得奖品,在奖品不够的情况下,重新抽奖,再将其发放.直到奖品数量为余量值后,结束抽奖
    

    其中,在github上找到一个非常厉害的抽奖,实现,不过,鉴于项目简单性,就没拿过来了,自己写个工具类算了,毕竟.怎么方便怎么来嘛

    思路:

    在给定奖品概率情况下,可以得出总的奖品概率,用我在10年前学过的数学知识来说,在一个一维象限,每个概率都会占有一定的长度,在随机生成一个0-N 数值
    
    将其插入其这个一维象限中,得到的索引就是好了.
    
    
    ........部分代码....//结束条件,获取剩余数量<=0 遍历所有        do{
    
                int type = RandomUtils.nextInt(0, giftArray[maxSet.size()-1]+1);
                List<GiftEntity> giftsEnums = new CopyOnWriteArrayList<>(giftsIntoDate);
                //去掉非类型礼品
                for (GiftEntity giftsEnum : giftsEnums) {
                    if (type != giftsEnum.getType()) {
                        giftsEnums.remove(giftsEnum);
                    }
                }
                int index = LotteryUtil.drawGift(giftsEnums);
                if(index == -1){
                    return null;
                }
                //结果奖品
                giftEntity = giftsEnums.get(index);
                //将奖品id放入set中,判断是否全部发放完毕
                Integer id = giftEntity.getId();
    //            //在set里面,都是数量为0的不进行下一步操作
                boolean contains = giftSet.contains(id);
                if(contains){
                    continue;
                }
                count = (Integer) redisService.get(GIFT_CACHE + id);
                GiftEntity giftEntityById = null;
                //缓存失效  出现的情况可能是...
                if(count == null){
                    //获取redis锁
              // ... lockData
    //走数据库 giftEntityById = giftDao.findGiftEntityById(id); if(!Objects.isNull(giftEntityById)){ //数据库 synchronized (lockData){ count = giftEntityById.getNumber(); if(count > 0){ giftEntityById.setNumber(count - 1); giftDao.save(giftEntityById); }else{ count = 0; } } }else{ count = 0; } } //查看缓存中资源数量是否<0 if (count > 0 && count != 0) { //判断走缓存还是数据库 if(!Objects.isNull(redisService.get(GIFT_CACHE + id))){ //缓存 //用户将缓存中的数量减一 } break; }else{ //控制次数 如果遍历每个奖品后,则今日奖品资源发放完毕 giftSet.add(id); } }while (count >= 0 && giftSet.size() < giftList); if (giftSet.size() == giftList) { return null; }

    以上便是部分代码逻辑代码,用户判断奖品数量,前M个是否必中,不中,在次循环抽取,,防止死循环,将其抽取结果放入HashSet中,判断礼品个数是否达到上限,就是规定的个数,如果hashset个数达到规定的个数,说明,已经全部都抽了一边了,这就说明,奖品数量已经没了,那就GG了,可以返回为空了,说明无奖可发.

    重点,抽奖概率:

    假设有4种奖品,其概率依次为以下所示:

     在计算下各个概率在一维象限中,所占比例,一次相加除总概率,算出来的

    随机概率用的是java自带的Math下的随机生成的,0-1之间,每次抽取后,再看在哪个区间内.其区间就是原有的概率索引

    比说说现在随机生成了     0.9740539834739117

    那这个就在  0.79    0.99 之间了,将其插入其中  

    0.79  <0.9740539834739117   <  0.99
    

    最后就成了

    0.7 < 0.79 < 0.9740539834739117 <0.99 < 1其

    其索引值是2,,原先我们定义的2下标下是0.19 则返回概率是0.19的奖品

    至此,OK

    代码如下:

    private static int draw(List<Double> giftProbList){
            if(CollectionUtils.isEmpty(giftProbList)){
                return -1;
            }
            List<Double> list = new ArrayList<>();
            // 随机生成一个随机数,做插入用
            double random = Math.random();
            // 计算概率总和
            Double sum =  giftProbList.stream()
                    .mapToDouble(d -> d)
                    .summaryStatistics()
                    .getSum();
            if(sum != 0){
                //概率所占比例
                double rate = 0D;
                for(Double prob : giftProbList){
                    rate += prob;
                    list.add(rate / sum);
                }
                list.add(random);
                Collections.sort(list);
                // 返回该随机数在比例集合中的索引
                return list.indexOf(random);
            }
            return -1;
        }

     模拟用户抽奖,假设有1000个用户

     RESULT:

    0.7:  抽取个数:701   概率:0.701
    0.1:  抽取个数:84    概率:0.084
    0.19: 抽取个数:198   概率:0.198
    0.01: 抽取个数:17    概率:0.017

     瞅着效果还行,不过我测试了几个极端的情况下,还是有点问题....那是啥问题呢,下次分解喽,要去干活了

    平凡是我的一个标签
  • 相关阅读:
    一起谈.NET技术,.Net Discovery系列之深入理解平台机制与性能影响(下) 狼人:
    一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(十二)魔法系统 狼人:
    一起谈.NET技术,.Net Discovery系列之深入理解平台机制与性能影响 (中) 狼人:
    一起谈.NET技术,再次分享一个多选文件上传方案 狼人:
    一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(十一)AI系统 狼人:
    一起谈.NET技术,.NET中的异步编程 IO完成端口以及FileStream.BeginRead 狼人:
    一起谈.NET技术,C#中标准Dispose模式的实现 狼人:
    一起谈.NET技术,DotNet并行计算的使用误区 狼人:
    一起谈.NET技术,.NET中的委托 狼人:
    一起谈.NET技术,ASP.NET MVC3 基础教程 – Web Pages 1.0 狼人:
  • 原文地址:https://www.cnblogs.com/guyanzy/p/11233642.html
Copyright © 2011-2022 走看看