分享三个简单的抽奖算法实现
t-jian 时间:2020-08-13
完整代码案例中使用了一个简单的工厂模式
/// <summary> /// 抽奖抽象类 /// </summary> public abstract class Draw { /// <summary> /// 奖品集合 /// </summary> public List<Prize> Prizes { get; set; } public Draw(List<Prize> prizes) { this.Prizes = prizes; } /// <summary> /// 进行抽奖 /// </summary> /// <returns></returns> public abstract Prize Play(); } /// <summary> /// 每几个人必中假设将A奖品中奖率设为10,那么第10位抽奖的用户必中,第20位抽奖用户必中,以此类推30、40...;当奖品中存在中奖率相同的则会优先发放数量多的奖品。 /// </summary> public class EachPersonMustDraw : Draw { public int CurrentPersonNum { get; set; } public EachPersonMustDraw(List<Prize> prizes, int num) : base(prizes) { this.CurrentPersonNum = num; } public override Prize Play() { var awardList = this.Prizes.Where(n => n.Balance > 0 && n.Rate > 0 && n.Type != 0).ToList(); if (awardList.Count < 1) { return null; } Prize prize = null; foreach (var item in awardList) { if (CurrentPersonNum % item.Rate == 0) { prize = item; break; } } return prize; } } /// <summary> /// 指定时间段内将奖品发完 /// 核心:将奖品分时间段派发,在每个时间段内随机一个时间节点与当前时间节点对比, /// 如果数据奖品释放节点就派发奖品,否则将继续等待抽奖者的到来 /// ps:奖品数量固定,但是参与抽奖的人数不可预知 ///假设将A奖品(14)、B奖品(10)在当天0点-23:59点段发放,奖品会均分再24小时里面; /// 假设当前为(0:10)那么在0-1点这个时间段里会有一个奖品随机发出,在奖品释放时间点之后的抽奖用户就有机会 拿到该时间段的奖品, ///如果奖品未被抽走,将继续等待抽奖者的到来,缺点:假如前段时间内没有用户前来抽奖, ///那么后面进来的用户必然会把前一个时间段释放的奖品抽走 /// </summary> public class TimeSectionDraw : Draw { public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public TimeSectionDraw(List<Prize> prizes, DateTime? startDate, DateTime? endDate) : base(prizes) { this.StartDate = startDate ?? DateTime.Now.Date; this.EndDate = endDate ?? DateTime.Now.Date.AddDays(1).AddSeconds(-1); } public override Prize Play() { var awardList = this.Prizes; //当前时间戳 Int64 now = Utils.GetCurrentTimeStamp(); //今天零点时间戳 Int64 startTime = Utils.DateFormatTimeStamp(this.StartDate); //当天23时59分59秒 Int64 endTime = Utils.DateFormatTimeStamp(this.EndDate); Prize prize = null; if (awardList == null || awardList.Count < 1) { return null; } //剩余的奖品数量 byte type = (byte)PrizeEnum.NoPrize; awardList = awardList.Where(n => n.Type != type).ToList(); int weight = awardList.Sum(n => n.Balance); if (weight == 0) { return null; } //以当前剩余的奖品数做为随机因子,确保奖品被依次被选出 int num = Utils.GetRandom(0, weight); int rand = num; foreach (var item in awardList) { num -= item.Balance; if (num < 0) { prize = item; break; } } int total = awardList.Sum(n => n.Amount); //时间段 Int64 dataTime = (endTime - startTime) / total; Int64 nextRan = Utils.GetRandom(0, (int)dataTime); //计算下一个奖品释放时间点,加上一个随机时间点确保奖品不是固定时间段 Int64 releaseTime = startTime + (total - weight) * dataTime + Math.Abs(nextRan) % dataTime; //当前时间未到达下一个奖品释放时间点 if (now < releaseTime) { return null; } return prize; } } /// <summary> /// 概率抽奖 以100为分母根据奖品概率划分奖品区间,所有概率总和不应超过100 /// </summary> public class RateDraw : Draw { public RateDraw(List<Prize> prizes) : base(prizes) { } public override Prize Play() { var awardList = Prizes.Where(n => n.Type != 0 && n.Balance > 0).ToList(); if (awardList == null || awardList.Count < 1) { return null; } List<Area> areas = new List<Area>(); double start = 0;// 区间开始 double end = 0;// 区间结束 foreach (var item in awardList) { end = start + item.Rate; areas.Add(new Area() { ID = item.PrizeUnid, Start = start, End = end }); start = end; } if (start < 100D) { areas.Add(new Area() { ID = null, Start = start, End = 100 }); } int index = Utils.GetRandom(0, 100); var pp = areas.Where(n => n.Start <= index && n.End >= index).FirstOrDefault(); if (pp != null && pp.ID != null) { return awardList.FirstOrDefault(n => n.PrizeUnid == pp.ID); } return null; } public class Area { public Guid? ID { get; set; } public double Start { get; set; } public double End { get; set; } } } /** * 简单工厂模式 * 进行构造抽奖处理 * */ public class DrawSimpleFactory { /// <summary> /// 创建一个抽奖 /// </summary> /// <param name="prizes">奖品</param> /// <param name="activity">当前活动数据</param> /// <param name="curPersonNum">当前参与人数</param> /// <returns></returns> public static Draw CreateDraw(List<Prize> prizes, DrawActivity activity, int curPersonNum) { Draw play = null; switch (activity.Policy) { case (int)PolicyEnum.EachMust: play = new EachPersonMustDraw(prizes, curPersonNum); break; case (int)PolicyEnum.TimeRegion: var st = activity.IsUpdatePrize ? null : (DateTime?)activity.StartTime; var et = activity.IsUpdatePrize ? null : (DateTime?)activity.EndTime; play = new TimeSectionDraw(prizes, st, et); break; case (int)PolicyEnum.RandomRate: play = new RateDraw(prizes); break; default: play = new RateDraw(prizes); break; } return play; } } //奖品实体 public partial class Prize { public System.Guid PrizeUnid { get; set; } //名称 public string Name { get; set; } //奖品类型 public byte Type { get; set; } //对应类型的扩展参数 public string TypeExtend { get; set; } //剩余数量 public int Balance { get; set; } //总数量 public int Amount { get; set; } //中奖率 public double Rate { get; set; } }
最后调用传入参数即可,返回null 则代表没有中奖~~~~
Prize prize = DrawSimpleFactory.CreateDraw(prizes, activity, drawTotal).Play();
特别声明:本站部分内容收集于互联网是出于更直观传递信息的目的。该内容版权归原作者所有,并不代表本站赞同其观点和对其真实性负责。如该内容涉及任何第三方合法权利,请及时与824310991@qq.com联系,我们会及时反馈并处理完毕。