抽奖,是很多企业、聚会的常见玩乐形式,光彩绚丽的抽奖屏幕背后,是计算程序+抽奖用户信息。程序=算法+数据结构。
好,说抽奖程序的的实现吧。这个实现一般需要应用数学原理。而本文的方法是我在参加一次婚礼的抽奖体验后突然想到的,一种比较简单、无需数学原理的方法。
功能:能按照相同概率,从用户集合中抽出随机的部分用户集合作为中奖者。抽奖可以进行多次,对已中奖的用户不会重复抽取。
使用技术:
1.SqlServer数据库,使用NewID()作为select随机筛选函数
2.sql随机函数
3.为了快速方便的搭建原型,使用EF 4.1 DB first,然后代码生成Model类(我在快速开发原型是喜欢EF,讨厌sql-实体转换。能让我从sql中解放出来,专注业务逻辑。只有
当需要的sql逻辑比较复杂,EF难以实现或效率不行时我才写sql-实体转换)。
主要逻辑:
1.建立LotteryUsers(抽奖用户的英文词组)表,定义用户信息、以及是否已中过奖的标志位IsLotteried,默认都是false-0(IsDelete-该用户是否已删除、无效)。
2.执行sql查询出定量的中奖用户集合 :
SELECT TOP(@chooseCount) * FROM LotteryUsers lu WHERE lu.IsDeldte = 0 AND lu.IsLotteried = 0 ORDER BY NEWID()
并且将这些中奖用户的IsLotteried字段设置为true-1
3.控制台显示中奖的用户集合
DB建表语句:
1 CREATE TABLE [dbo].[LotteryUsers]( 2 [Id] [int] IDENTITY(1,1) NOT NULL, 3 [JobNumber] [nvarchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL, 4 [Name] [nvarchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL, 5 [IsLotteried] [bit] NOT NULL, 6 [IsDeldte] [bit] NOT NULL, 7 CONSTRAINT [PK_LotteryUsers] PRIMARY KEY CLUSTERED 8 ( 9 [Id] ASC 10 )
结构、示例数据:
程序结构:
代码(使用EF 4.1,实体生成模型),代码细节、EF技术看不懂没关系,了解原理即可:
1 static void Main(string[] args) 2 { 3 //添加测试数据:用户信息 4 ////using (LotteryContext context = new LotteryContext()) 5 ////{ 6 // for (int i = 1; i < 101; i++) 7 // { 8 // LotteryUser user = new LotteryUser 9 // { 10 // JobNumber = i.ToString("00000"), 11 // Name = "niu"+i.ToString(), 12 // IsLotteried = false, 13 // IsDeldte = false, 14 // }; 15 // context.LotteryUsers.Add(user); 16 // } 17 // context.SaveChanges(); 18 //} 19 20 //从用户集合中随机抽取部分中奖用户显示 21 var lotteryUsers = GetPrizeUsersAndUpdatePrized(10); 22 foreach (var lotteryUser in lotteryUsers) 23 { 24 Console.WriteLine(string.Join(",", lotteryUser.Id, lotteryUser.JobNumber, lotteryUser.Name)); 25 } 26 27 } 28 29 /// <summary> 30 /// 从用户集合中随机抽取部分中奖用户,并将这些用户更新为已中奖、下次不会重复中奖 31 /// </summary> 32 /// <param name="chooseCount">指定中奖用户个数</param> 33 public static List<LotteryUser> GetPrizeUsersAndUpdatePrized(int chooseCount) 34 { 35 List<LotteryUser> lotteryUsers = new List<LotteryUser>(); 36 using (LotteryContext context = new LotteryContext()) 37 { 38 39 string lotteryUsersSql = @"SELECT TOP(@chooseCount) * FROM LotteryUsers lu 40 WHERE lu.IsDeldte = 0 AND lu.IsLotteried = 0 41 ORDER BY NEWID()"; 42 lotteryUsers = context.LotteryUsers.SqlQuery(lotteryUsersSql 43 , new SqlParameter("@chooseCount", chooseCount)).ToList(); 44 //foreach (var lotteryUser in lotteryUsers) 45 //{ 46 // Console.WriteLine(string.Join(",", lotteryUser.Id, lotteryUser.JobNumber, lotteryUser.Name, lotteryUser.IsLotteried, lotteryUser.IsDeldte)); 47 //} 48 49 //更新已得过奖状态为:是 50 foreach (var lotteryUser in lotteryUsers) 51 { 52 lotteryUser.IsLotteried = true; 53 } 54 context.SaveChanges(); 55 56 return lotteryUsers; 57 } 58 }
就这样,lotteryUsers即是中奖的用户。若要继续抽奖,只要再调用GetPrizeUsersAndUpdatePrized()即可,中奖用户不会重复。
---------------------------------------------备注、其他----------------
1.对于某些抽奖,若要内定某些VIP中某个奖,最好不要写死、在配置文件中写上VIP的名字,在抽奖函数中传入这些VIP的名单、直接确认。
2.对于某些次VIP们,若需要提高中奖概率,需要待议。可能实现的方法有:在users表中加上权重Pritory值,然后再做某些操作;或者采用其他较复杂的数学实现方法。
3.我的程序不注重程序\DB效率,所以未经优化。但是实际上,一般抽奖用户也就1W人以下,完全不存在性能问题。(中国16亿人全名抽奖,淘宝京东等全站抽奖数据量很大的,需要另行优化)。