關於抽獎,日常用到的非常頻繁,大大小小,方方面面都有關聯,
一 抽獎活動設置
萬事萬物都是建立在數據之上,有抽獎,就有限制,先簡單羅列下抽獎活動的一些基本條件,如下圖所示
二 算法選擇與設計
主流的採用 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); }
今天先寫到這裏