首页 Web 分享三个简单的抽奖算法实现

分享三个简单的抽奖算法实现

完整代码案例中使用了一个简单的工厂模式

  /// <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联系,我们会及时反馈并处理完毕。