伪大厂抽奖玩法与并发处理

      关于抽奖,日常用到的非常频繁,大大小小,方方面面都有关联,

一 抽奖活动设置

万事万物都是建立在数据之上,有抽奖,就有限制,先简单罗列下抽奖活动的一些基本条件,如下图所示

二 算法选择与设计

       主流的采用 AliasMethod 算法,采用散点命中的方式,支持不同奖品的命中概率计算,使用起来也较为方便,但是前提要样本量足够大,三几十人的情况下,不建议此算法,可直接采用一般随机抽奖即可,所以针对不同体量,不同业务场景要做两手准备,保不齐这些动则十几万的企业开放了一个只有十几个人员的部门内抽奖活动,否则就尴尬了,一直反馈 “真-无法中奖” ,这里简单设计一下 真-随机抽奖

 

 

主流程代码:

                //计算奖品概率之和 
                var rates = Draw.TotalWinningRate / 100;
                //拉大中奖区间
                var scope = LibSysUtils.ToInt16(Math.Ceiling(Length / rates));
                //获取随机数区间
                var awardScope = GetFixRandomNumber(scope, Draw, out Dictionary<int, Draw> dic);
                //生成随机数
                var randomNumber = random.Next(0, scope);
                //判断是否中奖
                var lucky = awardScope.Contains(randomNumber);
                if (lucky)
                {
                    dic.TryGetValue(randomNumber, out Draw awardPrize);
                    return awardPrize ?? result;
                }
                return result;

算法代码: 

        /// <summary>
        /// 指定区间内不重复的随机数集合
        /// </summary>
        /// <param name="scope">中奖区间下标</param>
        /// <param name="drawPrizes">奖池</param>
        /// <param name="dic">中奖字典</param>
        /// <returns></returns>
        public List<int> GetRandomNumber(int scope, List<Draw> drawPrizes, out Dictionary<int, Draw> dic)
        {
            var list = new List<int>();
            dic = new Dictionary<int, Draw>();
            var random = new Random(GetRandomSeed()); //初始化随机对象
            var index = random.Next(0, scope);        //生成随机数
            var lenth = drawPrizes.Count; 
            list.Add(index);                          //随机数集合第一个值
            dic.Add(index, drawPrizes[0]);            //随机数区间第一个字典
            int i = 0;
            while (list.Count < lenth)
            {
                i++;
                var temp = RandomNumber(list, scope);
                list.Add(temp);                       //区间内随机数集合
                dic.Add(temp, drawPrizes[i]);         //区间内随机数-奖品字典集合
            }
            return list;
        }

区间内不同随机数-生成方法

        //获取置顶区间置顶长度内不重复的随机数
        public int RandomNumber(List<int> list, int scope)
        {
            //注释递归算法-  
//这里有大坑: 极短时间内,Random生成的对象大部分都是相同值,如果采用递归获取不同值,很有可能陷入死循环,占满线上服务资源,造成重大事故
//var random = new Random(); //var temp = random.Next(0, scope); //if (list.Contains(temp)) //{ // return RandomNumber(list, scope, random); //}

//采用普通的随机数生成,循环次数为区间内次数 int index = 0; while (index < scope) { index++;
//生成随机数时,初始化随机对象,保证生成数字不唯一,亲测有效
var temp = new Random(GetRandomSeed()).Next(0, scope); if (list.Contains(temp)) { continue; } return temp; } for (int i = scope - 1; i >= 0; i--) { if (list.Contains(i)) { continue; } return i; } return 0; }

初始化随机对象

        private int GetRandomSeed()
        {
            byte[] bytes = new byte[4];
            System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
            rng.GetBytes(bytes);
            return BitConverter.ToInt32(bytes, 0);
        }

 

 今天先写到这里

     

posted @ 2023-02-15 15:18  郎中令  阅读(75)  评论(0编辑  收藏  举报