不确定人数的抽奖方法

Posted 深蓝居

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不确定人数的抽奖方法相关的知识,希望对你有一定的参考价值。

很多年前就给前公司的年会做过年会抽奖,基本要求就是年会入场时签到,签到的员工都参与抽奖(也可以设置公司高管过滤,不参与抽奖),奖品是预设好的,到时候就是给所有签到员工编号,然后抽奖过程中不断生成一组随机数,这些随机数对应的编号的员工姓名和照片就显示出来,这是很容易想到的算法。

但是还要一种情况就是互联网模式的抽奖,有点像双十一之前,阿里派发红包一样,大家都可以在开始抽奖的时候去抽,奖品也是预设好的,比如1000W的奖金池,派发完毕就抽奖完毕,每个用户可以抽取多次。这种抽奖方式主要是应对抽奖人数不确定的情况,谁也不需要提前签到报名,到了抽奖时间只要注册用户都可以抽奖。

因为抽奖人数不确定,所以采用一人多次抽奖的方案是很好的,对用户来说也是,如果第一次没有抽中,还可以尝试第二次,第三次。具体算法上,其实更简单,因为用户点击抽奖的顺序是随机的,所以我们连随机数都不用用,直接给用户的一次抽奖请求编个自增的号,如果这个号满足中奖规则,那么就分配礼品,返回该抽奖请求中奖结果,如果不满足中奖规则,那么我们就返回未中奖。

为了避免用户频繁的点击,造成服务器过高的负担,我们可以在客户端设置一个动画过程,比如转盘抽奖,可以转几秒以后才请求服务器,看是否中奖,对用户来说也增加了趣味性。为了避免用户不通过客户端,直接发起频繁的HTTP请求来刷奖,我们甚至可以在服务器设置同一个用户的请求时间间隔。

下面贴出我写的一个示例代码部分,我设置了一个自增的整数Sequence ,每个正常的抽奖请求,则Sequence ++,另外设置默认的抽奖基数baseNumber=100,如果能够Sequence能够被baseNumber整除,那么就中奖,否则不中奖:

[RoutePrefix("api/Lottery")] 
  public class LotteryController : AbpApiController 
  {

      private static volatile int Sequence = 1; 
      private static IList<int> winnerList=new List<int>(); 
      /// <summary> 
      /// 抽奖开始标记,请通过StartNewLotteryRound打开 
      /// </summary> 
      private static bool start = false; 
      /// <summary> 
      /// 所有产品都被抽完了的标记 
      /// </summary> 
      private static bool allPrizeOut = false; 
      /// <summary> 
      /// 当前轮次ID 
      /// </summary> 
      private static int currentRoundId = 0; 
      public ILotteryAppService LotteryAppService { get; set; } 
      /// <summary> 
      /// 抽奖基数,只要被该数整除就中奖 
      /// </summary> 
      private static int baseNumber =100; 
      private static IDictionary<int,DateTime> userDrawTime=new Dictionary<int, DateTime>();

      private bool CheckUserDrawTime(int userId) 
      { 
          if (userDrawTime.ContainsKey(userId)) 
          { 
              return userDrawTime[userId].AddSeconds(8) < DateTime.Now;//8s后可以抽奖 
          } 
          else 
          { 
              return true; 
          } 
      } 
      /// <summary> 
      /// 抽奖一次 
      /// </summary> 
      /// <param name="userId"></param> 
      /// <returns></returns> 
      [HttpGet] 
      [Route("Draw/{userId}")] 
      public DrawResult Draw(int userId) 
      { 
          if (!start) 
          { 
              return new DrawResult(400,0, "抽奖未开始"); 
          } 
          if (allPrizeOut) 
          { 
              return new DrawResult(400, 0, "所有奖品已抽完"); 
          } 
          if (!CheckUserDrawTime(userId)) 
          { 
              return new DrawResult(400, 0, "请求过于频繁,请稍后再试"); 
          }

          int myNumber = Sequence++; 
          userDrawTime[userId] = DateTime.Now;//记录用户这次抽奖的时间

          if (myNumber%baseNumber == 0) //中奖啦! 
          { 
              if (winnerList.Contains(userId)) 
              { 
                  //用户已经中奖,不用再抽 
                  return new DrawResult(200, 0, "您已经中过奖了"); 
              } 
              var result = LotteryAppService.WriteAWinner(userId, currentRoundId);

              switch (result.ExceptionType) 
              { 
                  case LotteryExceptionType.NoException: 
                  { 
                      winnerList.Add(userId);

                      return new DrawResult(200, result.PrizeId, ""); 
                  } 
                  case LotteryExceptionType.AllPrizeOut: 
                  { 
                      allPrizeOut = true; 
                      return new DrawResult(400, 0, "所有奖品已抽完");

                  } 
                  case LotteryExceptionType.InvalidLotteryRound: 
                  { 
                      return new DrawResult(400, 0, "抽奖轮次无效"); 
                  } 
                  default: 
                  { 
                      return new DrawResult(400, 0, "当前用户无效"); 
                  }

              }

          } 
          return new DrawResult(200, 0, "");

      }


      /// <summary> 
      /// 获得我的奖品对象 
      /// </summary> 
      /// <param name="userId"></param> 
      /// <returns></returns> 
      [HttpGet] 
      [Route("MyPrize/{userId}")] 
      public IList<LotteryDto> GetMyPrize(int userId) 
      { 
          return LotteryAppService.GetMyPrize(userId); 
      }

      /// <summary> 
      /// 开始新一轮的抽奖 
      /// </summary> 
      /// <param name="roundId"></param> 
      [HttpPost] 
      [Route("StartNewLotteryRound")] 
      [AbpApiAuthorize(PermissionNames.Admin)] 
      public bool StartNewLotteryRound(int roundId) 
      { 
       
          start = true; 
          allPrizeOut = false; 
          currentRoundId = roundId; 
          return true; 
      } 
      /// <summary> 
      /// 获得当前轮次的奖品和获奖者 
      /// </summary> 
      /// <returns></returns> 
      [HttpGet] 
      [Route("")] 
      public IList<LotteryDto> GetLotteries() 
      { 
        return  LotteryAppService.GetLotteries(currentRoundId); 
      } 
      /// <summary> 
      /// 获得所有的奖品和获奖者 
      /// </summary> 
      /// <returns></returns> 
      [HttpGet] 
      [Route("All")] 
      public IList<LotteryDto> GetAllLotteries() 
      { 
          return LotteryAppService.GetLotteries(0); 
      } 
      /// <summary> 
      /// 清空中奖结果,各种缓存 
      /// </summary> 
      /// <returns></returns> 
      [HttpPost] 
      [Route("Clean")] 
      [AbpApiAuthorize(PermissionNames.Admin)] 
      public bool Clean() 
      { 
          Sequence = 1; 
          start = false; 
          winnerList.Clear(); 
          LotteryAppService.CleanLotteries(); 
          return true; 
      } 
      /// <summary> 
      /// 获取是否显示抽奖图标 
      /// </summary> 
      /// <returns></returns> 
      [HttpGet] 
      [Route("ShowLotteryIcon")] 
      public bool GetShowLotteryIcon() 
      { 
          return LotteryAppService.ShowLotteryIcon; 
      } 
      /// <summary> 
      /// 设置是否显示抽奖图标 
      /// </summary> 
      /// <param name="show"></param> 
      /// <returns></returns> 
      [HttpPut] 
      [Route("ShowLotteryIcon/{show}")] 
      public HttpResponseMessage SetShowLotteryIcon(bool show) 
      { 
         
          try 
          { 
              LotteryAppService.ShowLotteryIcon = show; 
              return Request.CreateResponse(HttpStatusCode.OK, true); 
          } 
          catch (Exception ex) 
          { 
              var resp = new HttpResponseMessage(HttpStatusCode.BadGateway) 
              { 
                  Content = new StringContent("设置ShowLotteryIcon失败:" + ex.Message), 
                  ReasonPhrase = "Gateway failed" 
              }; 
              throw new HttpResponseException(resp); 
          } 
      }

   
      /// <summary> 
      /// 设置Base Number 
      /// </summary> 
      /// <param name="number"></param> 
      /// <returns></returns> 
      [HttpPut] 
      [AbpApiAuthorize(PermissionNames.Admin)] 
      [Route("SetBaseNumber/{number}")] 
      public bool SetBaseNumber(int number) 
      { 
          baseNumber = number; 
          return true; 
      } 
  }

 

以上是关于不确定人数的抽奖方法的主要内容,如果未能解决你的问题,请参考以下文章

A1124 Raffle for Weibo Followers (20分)

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段

PHP解决抢购抽奖等阻塞式高并发库存防控超量的思路方法

转(解决抢购秒杀抢楼抽奖等阻塞式高并发库存防控超量的思路方法)

PHP解决抢购秒杀抢楼抽奖等阻塞式高并发库存防控超量的思路方法

PHP解决抢购秒杀抢楼抽奖等阻塞式高并发库存防控超量的思路方法