JAVA基于权重的抽奖

您所在的位置:网站首页 网络层主要协议有哪些主要功能是什么 JAVA基于权重的抽奖

JAVA基于权重的抽奖

2023-08-24 17:27| 来源: 网络整理| 查看: 265

 

 https://blog.csdn.net/huyuyang6688/article/details/50480687

 

如有4个元素A、B、C、D,权重分别为1、2、3、4,随机结果中A:B:C:D的比例要为1:2:3:4。

总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1)、[1,3)、[3,6)、[6,10)。然后随机出一个[0,10)之间的随机数。落在哪个区间,则该区间之后的元素即为按权重命中的元素。

实现方法:利用TreeMap,则构造出的一个树为:            B(3)    /      \         /           \     A(1)       D(10)                    /                  /             C(6)

然后,利用treemap.tailMap().firstKey()即可找到目标元素。

(3落在70%权重,7落在70-95的权重,10落在95-100的权重)

https://www.bbsmax.com/A/LPdo4pk2z3/

 

 

三、依赖不可控的物理随机数 

什么意思呢,先看个图,看完你就知道了

 

明白了吧,呵呵,这就是现如今灰常流行的一种抽奖算法,绝对公平、绝对透明、绝对木有暗箱(除非偷偷给你换了抽奖号码)!但是这种方法唯一的缺点是无法实时抽奖,只能事后抽奖。也就是只能拿个抽奖号等着上帝的眷顾,阿门。。。

 

 

例如游戏中打败一个boss,会掉落下面其中一个物品,而每个物品都有一定概率: 1. 靴子 20% 2. 披风 25% 3. 饰品 10% 4. 双手剑 5% 5. 金币袋 40% 现在的问题就是如何根据概率掉落一个物品给玩家。

一. 一般算法:生成一个列表,分成几个区间,例如列表长度100,1-20是靴子的区间,21-45是披风的区间等,然后随机从100取出一个数,看落在哪个区间。算法时间复杂度:预处理O(MN),随机数生成O(1),空间复杂度O(MN),其中N代表物品种类,M则由最低概率决定。

二、离散算法:也就是上面的改进,竟然1-20都是靴子,21-45都是披风,那抽象成小于等于20的是靴子,大于20且小于等于45是披风,就变成几个点[20,45,55,60,100],然后也是从1到99随机取一个数R,按顺序在这些点进行比较,知道找到第一个比R大的数的下标,比一般算法减少占用空间,还可以采用二分法找出R,这样,预处理O(N),随机数生成O(logN),空间复杂度O(N)。 请点击查看详细:http://www.cnblogs.com/miloyip/archive/2010/04/21/1717109.html

三、Alias Method Alias Method就不太好理解,实现很巧妙,推荐先看看这篇文章:http://www.keithschwarz.com/darts-dice-coins/ 大致意思:把N种可能性拼装成一个方形(整体),分成N列,每列高度为1且最多两种可能性,可能性抽象为某种颜色,即每列最多有两种颜色,且第n列中必有第n种可能性,这里将第n种可能性称为原色。 想象抛出一个硬币,会落在其中一列,并且是落在列上的一种颜色。这样就得到两个数组:一个记录落在原色的概率是多少,记为Prob数组,另一个记录列上非原色的颜色名称,记为Alias数组,若该列只有原色则记为null。

之前的例子,为了便于演示换成分数 1. 靴子 20% -> 1/4 2. 披风 25% -> 1/5 3. 饰品 10% -> 1/10 4. 双手剑 5% -> 1/20 5. 金币袋 40% -> 2/5 然后每个都乘以5(使每列高度为1),再拼凑成方形 拼凑原则:每次都从大于等于1的方块分出一小块,与小于1的方块合成高度为1

由上图方形可得到两个数组: Prob: [3/4, 1/4, 1/2, 1/4, 1] Alias: [4, 4, 0, 1, null] (记录非原色的下标)

之后就根据Prob和Alias获取其中一个物品 随机产生一列C,再随机产生一个数R,通过与Prob[C]比较,R较大则返回C,反之返回Alias[C]。

Alias Method 复杂度:预处理O(NlogN),随机数生成O(1),空间复杂度O(2N)

https://www.cnblogs.com/younggun/p/3249772.html   简介

最近闲的无聊,将以前做的一个微信抽奖小demo拿来分享一下,以便加深印象。

效果图   1.gif 业务要求

在一个奖池中放一堆奖品,分别给它们设置不同的数量,比如一等奖A10个,二等奖B,30个,三等奖C10个,然后设置参与人数C人 条件是: 当奖品数大于参与人数,100%中奖。 当奖品A发放完是,不能被抽中。 当奖品发放完毕是,该抽奖活动自动下架。 同一个用户如果中奖了,将不能继续参与该活动。 这里只讨论下其中的核心算法的设计及一个示例函数,算法之外的系统控制暂不提及。 实现抽奖的方法应该有很多,没有仔细去考察和搜索那些非常复杂的算法,这里仅做了一个简单的假设,并在此基础上推出后面所有的控制逻辑。

实现方法

java核心抽奖代码如下:

public class LotteryUtil { /** * * @param orignalRates 传人每一个奖品概率的集合,(集合的第一个参数是0.0 表示百分比中奖) * @return */ public static int lottery(List orignalRates) { if (orignalRates == null || orignalRates.isEmpty()) { return -1; } int size = orignalRates.size(); // 计算总概率,这样可以保证不一定总概率是1 double sumRate = 0d; for (double rate : orignalRates) { sumRate += rate; } // 计算每个物品在总概率的基础下的概率情况 List sortOrignalRates = new ArrayList(size); Double tempSumRate = 0d; /*遍历奖品概率的集合,计算每一个奖品的中间区间*/ for (double rate : orignalRates) { tempSumRate += rate; sortOrignalRates.add(tempSumRate/sumRate); } // 根据区块值来获取抽取到的物品索引 double nextDouble = Math.random(); sortOrignalRates.add(nextDouble); Collections.sort(sortOrignalRates); return sortOrignalRates.indexOf(nextDouble); } }

 

抽奖的业务逻辑代码如下 /*awardItems获取奖品的一个集合*/ if (activityUserDao.getCountByOpenId(Award.WHEEL_AWARD_TYPE, wid, open_id) 0) {// 中奖 Award a = awardItems.get(index - 1); long key = Math.round(Math.random() * (999999 - 100000) + 100000); // 6位数中奖序列号 // 修改商品剩余数量 + 记录序列号 if (awardDao.doLowerOverCount(a.getAward_id()) > 0 && activityUserDao.doInsert(new ActivityUser(aw.getPublic_id(), Award.WHEEL_AWARD_TYPE, wid, a.getAward_id(), key + "", open_id)) > 0) { rb.setCode(index); rb.setData(key); rb.setMessage(a.getAward_name()); } else { rb.setCode(0); } } // 抽奖记录 activityRecordDao.doInsert(new ActivityRecord(open_id, Award.WHEEL_AWARD_TYPE, wid, request.getRemoteAddr()));

 

前端抽奖工具类 /** * 注意:本插件运用了rem屏幕适配方案,一律采用rem作为单位,若项目中不是采用这种方案的,此段代码不会影响功能使用,仅会影响控件样式 */ (function(win, doc, $) { var defaultOpt = { rotateNum: 5, //转盘转动圈数 body: "", //大转盘整体的选择符或zepto对象 disabledHandler: function() {}, //禁止抽奖时回调 clickCallback: function() {}, //点击抽奖按钮,再次回调中实现访问后台获取抽奖结果,拿到抽奖结果后显示抽奖画面 KinerLotteryHandler: function(deg) {} //抽奖结束回调 }; function KinerLottery(opts) { this.opts = $.extend(true, {}, defaultOpt, opts); this.doing = false; this.init(); } KinerLottery.prototype.setOpts = function(opts) { this.opts = $.extend(true, {}, defaultOpt, opts); this.init(); }; KinerLottery.prototype.init = function() { var self = this; this.defNum = this.opts.rotateNum * 360; //转盘需要转动的角度 // console.log(this.defNum); // alert(this.defNum); //点击抽奖 $('#box').on('click', ".KinerLotteryBtn", function() { if($(this).hasClass('start') && !self.doing) { self.opts.clickCallback.call(self); } else { var key = $(this).hasClass('no_start') ? "noStart" : $(this).hasClass('completed') ? "completed" : "illegal"; self.opts.disabledHandler(key); } }); $(this.opts.body).find('.KinerLotteryContent').get(0).addEventListener('webkitTransitionEnd', function() { self.doing = false; var deg = $(self.opts.body).attr('data-deg'); if(self.opts.direction == 0) { $(self.opts.body).attr('data-deg', 360 - deg); $(self.opts.body).find('.KinerLotteryContent').css({ '-webkit-transition': 'none', 'transition': 'none', '-webkit-transform': 'rotate(' + (deg) + 'deg)', 'transform': 'rotate(' + (deg) + 'deg)' }); self.opts.KinerLotteryHandler(360 - deg); } else { $(self.opts.body).attr('data-deg', deg); $(self.opts.body).find('.KinerLotteryContent').css({ '-webkit-transition': 'none', 'transition': 'none', '-webkit-transform': 'rotate(' + (-deg) + 'deg)', 'transform': 'rotate(' + (-deg) + 'deg)' }); self.opts.KinerLotteryHandler(deg); } }); }; KinerLottery.prototype.goKinerLottery = function(_deg) { if(this.doing) { return; } var deg = _deg + this.defNum; var realDeg = this.opts.direction == 0 ? deg : -deg; this.doing = true; $(this.opts.body).find('.KinerLotteryBtn').addClass('doing'); $(this.opts.body).find('.KinerLotteryContent').css({ '-webkit-transition': 'all 5s', 'transition': 'all 5s', '-webkit-transform': 'rotate(' + (realDeg) + 'deg)', 'transform': 'rotate(' + (realDeg) + 'deg)' }); $(this.opts.body).attr('data-deg', _deg); }; win.KinerLottery = KinerLottery; })(window, document, $);

 

前端js调用抽奖类 /** * @author wjb * @description * @version 1.0.0 2017/2/11 */ app.controller("wheelOneController", ['$scope', '$stateParams', '$neu_', 'awardService', '$filter', '$timeout', 'util.alert', 'cfg', 'wxService', function($scope, $stateParams, $neu_, awardService, $filter, $timeout, alert, cfg, wxService) { /*中奖开始时间*/ $scope.wheelStatu = { start: true, noStart: false, completed: false } /*错误信息提示*/ $scope.errorMsg = ""; /*定义奖品数据变量*/ $scope.awards = []; /*活动的id和活动的微信公众号ID*/ var activity_id = $neu_.isEmpty($stateParams.activity_id) ? 1 : $stateParams.activity_id; var public_id = $neu_.isEmpty($stateParams.public_id) ? 1 : $stateParams.public_id; var open_id = $neu_.isEmpty($stateParams.open_id) ? cfg.openId : $stateParams.open_id; cfg.public_id = public_id; cfg.open_id = open_id; cfg.activity_id = activity_id; //alert(cfg.public_id+"=="+cfg.activity_id+"=="+cfg.open_id ); /*获取活动信息*/ wxService.setConfig(); awardService.getWheelInfo(activity_id, public_id).then(function(res) { //console.dir(res) if(res.success) { $scope.wheelStatu.start = true; } else { if(res.code == 1 || res.code == 3) { $scope.wheelStatu.noStart = true; } else if(res.code == 2) { $scope.wheelStatu.completed = true; } $scope.errorMsg = res.msg; } $scope.wheelInfo = res.data; }); awardService.getAwards(activity_id, public_id).then(function(res) { $scope.awards = res.data; }); /*奖品预览*/ var result = []; $scope.showPic = function(pic) { if(result.length == 0) { $neu_.each($scope.awards, function(item) { result.push(cfg.resourcePath + item.img); }); } wxService.previewImage(cfg.resourcePath + pic, result); } /*中奖结果集*/ $scope.result = []; $scope.user = { user_name: '', phone: '', activity_id: activity_id, user_openid: open_id }; /** * 提交中奖人信息 */ $scope.submit = function() { $(".actBody_close").click(); $scope.isLoading = true; awardService.updateLotteryUser($scope.user).then(function(res) { $scope.isLoading = false; if(res.success) { alert('您的中奖信息已备案,我们的客服人员稍后会联系您,如何领取奖品'); } else { alert('提交失败'); } }) //alert('哈哈上当了吧^_^_^_^') } $scope.load = function() { $timeout(function() { ActBounced(); $("#layer").hide(); new KinerLottery({ rotateNum: 5, //转盘转动圈数 body: "#box", //大转盘整体的选择符或zepto对象 direction: 0, //0为顺时针转动,1为逆时针转动 disabledHandler: function(key) { switch(key) { case "noStart": $scope.$apply(function() { alert($scope.errorMsg); }); break; case "completed": $scope.$apply(function() { alert($scope.errorMsg); }); break; } }, //禁止抽奖时回调 clickCallback: function() { var this_ = this; //此处访问接口获取奖品 $scope.isLoading = true; awardService.startAweel(activity_id, open_id).then(function(res) { $scope.isLoading = false; if(isDebug){ this_.goKinerLottery(0); }else{ if(res.success) { var index = cfg.isDebug ? Math.floor(Math.random() * 5) : res.code; $scope.result = $filter('awardToAngle')(index, $scope.awards); if(index == 0) { this_.opts.disabledHandler("noStart"); } else { this_.goKinerLottery($scope.result[1]); } } else { alert(res.msg); } } }) }, //点击抽奖按钮,再次回调中实现访问后台获取抽奖结果,拿到抽奖结果后显示抽奖画面 KinerLotteryHandler: function(deg) { $("#smallActAdv").click(); } }); }, 500); /*分享授权的地址*/ $timeout(function(){ var share ={shareUrl:"weixin/oauth.html?isBase=true&type=6&a_id="+activity_id+"&p_id="+public_id+"&appid="+cfg.appId}; wxService.onMenuShareAppMessage(share); },3000) } }])

 

以上是抽奖的主要代码


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3