实现一个简单的概率发奖问题

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

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

原文地址:https://www.cnblogs.com/guyanzy/p/11233642.html

时间: 2024-10-10 16:27:26

实现一个简单的概率发奖问题的相关文章

对暑期行程的一个简单总结

真的是应了那么一句话:"行百里者半九十",原先还曾豪情壮志的想把blog搞得有声有色,得一个"恒"字勋章.现在看来,暑期将尽,更新无多,愧对了自己的那份心情. 好在亡羊补牢,把最近的一些行程做一个简单的总结,荒疏的事情,重新捡起. 6月22-7月14这段时间,基本上是连--沈两地通勤了,没办法了,接了"机器学习导论"这门小学期的课程,也算是一个小的尝试,头一次讲解,马虎不得.课件.作业.程序,事无巨细,尽量躬亲().这次课有了两个发现,一是学生学

一个简单的抽奖概率函数

一个简单的抽奖概率函数 摘要:该函数主要是判断产生的随机数所在的概率范围... 一个简单的抽奖概率函数: /**  * 简单的抽奖概率函数  * @param array $rewardArray 概率,如:$rewardArray = array(10, 20, 20, 30, 10, 10),对应各奖品的概率  * @return int    概率数组的下标  */ function luckDraw($rewardArray) { $sum = array_sum($rewardArra

写了一个简单可用的IOC

根据<架构探险从零开始写javaweb框架>内容写的一个简单的 IOC 学习记录    只说明了主要的类,从上到下执行的流程,需要分清主次,无法每个类都说明,只是把整个主线流程说清楚,避免陷入细节中.学习过程最大的收获,框架也是人写的,没学过感觉很神秘高端.现在看来大概率是,未知往往觉得是高不可攀.http://naotu.baidu.com/file/6c3da879a4495b6bd369f71dcb726f05?token=ed8c0d49d4ee7bbd 原文地址:https://ww

实现一个简单的Unity网络同步引擎——netgo

实现一个简单的Unity网络同步引擎Netgo 目前GOLANG有大行其道的趋势,尤其是在网络编程方面.因为和c/c++比较起来,虽然GC占用了一部分机器性能,但是出错概率小了,开发效率大大提升,而且应用其原生支持的协程很容易就能开发出高并发的服务端程序.笔者接触VR行业两年有余,接触了一些商业unity网络引擎,总觉的用的东西都落伍了,于是自己写了一个简单的引擎.目前实现了的基本功能: 支持房间概念. 支持灵活的数据同步方式,包括帧同步和RPC. 支持自定义事件的发送. 也实现了一个简单的de

一个简单的主机管理模拟程序

最近写的一个小练习,主要是把前面学的东西整合一下.写了一个简单的主机管理界面,主要是练习以下知识点: Session和Cookie进行登录验证(装饰器) 数据库的基本操作 (单表,1对多,多对多) Form的简单使用实现验证 Bootstrap模板写个简单界面 自定义分页 信号,中间件,CSRF,模板语言,JavaScript,AJAX等等 界面比较low,毕竟不是专业的. 附件里面是Django的源代码,3个文件放在一起winrar解压就可以打开

Windows 上静态编译 Libevent 2.0.10 并实现一个简单 HTTP 服务器(无数截图)

[文章作者:张宴 本文版本:v1.0 最后修改:2011.03.30 转载请注明原文链接:http://blog.s135.com/libevent_windows/] 本文介绍了如何在 Windows 操作系统中,利用微软 Visual Studio 2005 编译生成 Libevent 2.0.10 静态链接库,并利用 Libevent 静态链接库,实现一个简单的 HTTP Web服务器程序:httpd.exe. 假设 Visual Studio 2005 的安装路径为“D:\Program

完成一个简单的时间片轮转多道程序内核代码

王康 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 " 分别是1 存储程序计算机工作模型,cpu执行程序的基础流程: 2 函数调用堆栈:各种寄存器和存储主要是为了指令的传取值,通过eip,esp,eax,ebp和程序内存的分区,搭配push pop call return leave等一系列指令完成函数调用操作. 3 中断:多道批程序! 在复习一下上一讲的几个重要指令

一个简单的税利计算器(网页版)

嗯嗯,做一个简单的网页版的税率计算器,功能比较简单,但是相对比较实用.因为参考了一些其他作品,所以在计算汇率的时候习惯性的是以美元做单位.具体的功能有着较为详细的标注.仅供大家学习参考下. <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>JavaScript Loan Calculator</title>

利用JSP编程技术实现一个简单的购物车程序

实验二   JSP编程 一.实验目的1. 掌握JSP指令的使用方法:2. 掌握JSP动作的使用方法:3. 掌握JSP内置对象的使用方法:4. 掌握JavaBean的编程技术及使用方法:5. 掌握JSP中数据库编程方法: 二.实验要求 : 利用JSP编程技术实现一个简单的购物车程序,具体要求如下. (1)用JSP编写一个登录页面,登录信息中有用户名和密码,分别用两个按钮来提交和重置登录信息. (2)编写一个JSP程序来处理用户提交的登录信息,如果用户名为本小组成员的名字且密码为对应的学号时,采用J