一個簡單抽獎算法的實現(xiàn)

最近的一個業(yè)務需求是開發(fā)一個抽獎管理功能阳似,要求在一個獎池中放一堆獎品闷堡,分別給它們設置不同的數(shù)量和概率隘膘,在獎品沒有發(fā)完的情況下,概率高的被抽中的幾率就大缚窿,反之則低棘幸。另外,概率為0的不能被抽中倦零,概率為100則一定要被抽中误续。

這里只討論下其中的核心算法的設計及一個示例函數(shù)吨悍,算法之外的系統(tǒng)控制暫不提及。

實現(xiàn)抽獎的方法應該有很多蹋嵌,沒有仔細去考察和搜索那些非常復雜的算法育瓜,這里僅做了一個簡單的假設,并在此基礎上推出后面所有的控制邏輯栽烂。

  1. 基本假設

假設系統(tǒng)生成一個1到100之間的隨機整數(shù)R躏仇,C為1到100之間的一個任意整數(shù),那么R小于C的概率為C/100腺办。

暫且不去從數(shù)學上論證這個假設是否真的成立焰手,我們僅從直觀上來看,R是隨機的怀喉,它的值不論是多少书妻,取到1-100之間任意一個整數(shù)的概率都是一樣的。

但C越小躬拢,R落入0-C之間的概率也會越小躲履,所以我們大致上可以用C來控制某個獎品的概率。Good!

  1. 實現(xiàn)邏輯

有了上面的假設聊闯,我們就可以考慮實現(xiàn)一個獎池內不同獎品配置情況下的控制邏輯工猜。

首先我們把獎池中的獎品做一個過濾,剔除掉那些不滿足條件的獎品菱蔬,比如概率為0的篷帅、已經發(fā)完的等等。剩下的獎品都是可以被抽中的拴泌,只是概率大小不同而已犹褒。

OK,下面我們來設置一些概念弛针,以方便后面的行文叠骑。

假設有N個獎品,它們都以100為滿概率削茁,那么它們總共的概率空間為O=N*100宙枷;
如果這N個獎品的概率分別為C1,C2,C3...Cn,那么他們總共的中獎概率空間就是H=C1+C2+C3+...+Cn茧跋,因為Cn總是小于等于100慰丛,所以
H總是小于等于O。

我們把以上這些參數(shù)在后臺配置好瘾杭,當抽獎行為發(fā)生時诅病,我們讓系統(tǒng)生成一個隨機數(shù)R,1<=R<=O,那么當R<=C時贤笆,我們就認為中獎了蝇棉,否則就不中獎。Good10!

在判斷出是否中獎后芥永,我們就可以進一步判斷中了什么獎篡殷。
首先把獎品以數(shù)組形式A按概率從小到大進行排序,然后求出每個獎品在總中獎概率空間H中的中獎區(qū)間埋涧,并且把各區(qū)間的最大值保存成一個數(shù)組D板辽。

例如有a和b兩個獎品,概率分別為20和30,那么a的概率空間為20棘催,中獎區(qū)間為1-20劲弦;而b的概率空間為30,但它的中獎區(qū)間是20-50醇坝,這樣D就是(1,20,50)瓶您。

然后我們再把D從小到大排序并循環(huán),當R小于20時纲仍,我們認為a被抽中;R小于50時贸毕,認為b被抽中郑叠。Good11!

這里有個問題,就是為什么不直接把R跟a和b的概率比較明棍,而要比較它們各自中間區(qū)間的最大值乡革?

因為我們設想的情況是,不僅某種獎品概率調大時其抽中的幾率增大摊腋,而且所有獎品的概率都調大時沸版,它們被抽中的幾率都增大。

如果直接把R跟獎品各自的概率比較兴蒸,根據我們上面的邏輯视粮,它們總的中獎空間H=2×100=200,只要R的值小于200橙凳,我們就已經認為中獎了蕾殴;但是當a和b兩種獎品的概率為99時,只有當R小于100時它們才會被抽中岛啸,R落在100到200之間將不被認為中獎钓觉,這顯然是不對的。

搞清楚了上面的邏輯坚踩,剩下的就是處理一些特殊情況了荡灾。
比如,如果某些獎品的概率為100,這就是我們之前說的存在滿概率獎品批幌。按我們的設想础锐,當有百分百中獎的獎品時,我們一定要這種獎品被抽中逼裆。

處理這個問題郁稍,我的方法是把獎品按概率分成兩組,一組是滿概率獎品胜宇,一組是非滿概率獎品耀怜。當滿概率獎品組不為空時,從中隨機取出一個作為被抽中的獎品放出桐愉,直到這些獎品被抽完财破。

到此為止整個邏輯基本結束,可以著手寫代碼了从诲。Good101!

/**
* 抽獎核心算法
* @param prize array,所有概率不為0且剩余數(shù)大于0的獎品數(shù)組 
* @return array 單個獎品
* version 2015.12.21
* author thinkmad@sina.com
*/
const FULL_CHANCE = 100;
function calcPrize($prize){
    
    if(!$prize){
        return false;
    }
    
    $arr_chance = array();//所有獎品概率
    $arr_delimiter = array();//中獎區(qū)間分界數(shù)組
    $full_chance_prize = $nofull_chance_prize = array();//劃分滿概率和非滿概率數(shù)組
    $H = 0;//中獎概率空間
    
    //劃分滿概率和非滿概率獎品
    foreach($prize as $item){
        if($item['prizeChance'] >= self::FULL_CHANCE) {
            $full_chance_prize[] = $item;
        }else{
            $nofull_chance_prize[$item['prizeID']] = $item;
        }
    }
    
    //存在滿概率獎品左痢,則隨機取出一個獎品并返回
    $len = count($full_chance_prize);
    if($len > 0){
        $r = mt_rand(0,$len-1);
        return $full_chance_prize[$r];
    }
    
    //計算總概率空間O
    $O = count($prize) * self::FULL_CHANCE;
    
    //計算總中獎空間H并生成概率數(shù)組
    foreach($nofull_chance_prize as $k => $v){
        $H += $v['prizeChance'];
        $arr_chance[$k] = $v["prizeChance"];
    }
    
    $R = mt_rand(1,$O);
    if($R > $H){ //R不在中獎空間
        return false;
    }else{//R落在中獎空間
        asort($arr_chance);
        for($i = 0; $i < count($arr_chance) ; $i++){
            $arr_delimiter[key($arr_chance)] = array_isum($arr_chance,0,$i+1);
            next($arr_chance);
        }
        foreach($arr_delimiter as $key => $val){
            if($R <= $val) {
                return $nofull_chance_prize[$key];
            }
        }
    }
}

/**
* 輔助函數(shù)array_isum,計算數(shù)組中i起n個數(shù)的和
* @params $input array,要計算的數(shù)組
* @params $start,int,起始位置
* @params $num,int,個數(shù)
* @return int
*/
function array_isum($input,$start,$num){
    $temp = array_slice($input, $start,$num);
    return array_sum($temp);
}

上面是用PHP實現(xiàn)的一個核心算法系洛,其中定義了一個常量FULL_CHANCE為100俊性。還定義了一個輔助函數(shù)array_isum,用來計算一個數(shù)組中從下標i開始n個數(shù)的和描扯。

這里面其實還有一個小小的問題定页,就是當幾種獎品都是非滿概率獎品且它們的概率相同,我們需要隨機抽出一個作為獎品放出绽诚,如果不做這個處理典徊,則會按照默認順序先把前面的獎品發(fā)完再發(fā)后面的。
Enjoy It!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末恩够,一起剝皮案震驚了整個濱河市卒落,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜂桶,老刑警劉巖儡毕,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扑媚,居然都是意外死亡妥曲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門钦购,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檐盟,“玉大人,你說我怎么就攤上這事押桃】” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長羡忘。 經常有香客問我谎痢,道長,這世上最難降的妖魔是什么卷雕? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任节猿,我火速辦了婚禮,結果婚禮上漫雕,老公的妹妹穿的比我還像新娘滨嘱。我一直安慰自己,他們只是感情好浸间,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布太雨。 她就那樣靜靜地躺著,像睡著了一般魁蒜。 火紅的嫁衣襯著肌膚如雪囊扳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天兜看,我揣著相機與錄音锥咸,去河邊找鬼。 笑死细移,一個胖子當著我的面吹牛搏予,可吹牛的內容都是我干的。 我是一名探鬼主播葫哗,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼球涛!你這毒婦竟也來了劣针?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤亿扁,失蹤者是張志新(化名)和其女友劉穎捺典,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體从祝,經...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡襟己,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了牍陌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擎浴。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毒涧,靈堂內的尸體忽然破棺而出贮预,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布仿吞,位于F島的核電站滑频,受9級特大地震影響,放射性物質發(fā)生泄漏唤冈。R本人自食惡果不足惜峡迷,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望你虹。 院中可真熱鬧绘搞,春花似錦、人聲如沸售葡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挟伙。三九已至楼雹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尖阔,已是汗流浹背贮缅。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工撞牢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留懈词,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓佣渴,卻偏偏與公主長得像齿坷,于是被迫代替她去往敵國和親桂肌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內容

  • 你的數(shù)學直覺怎么樣永淌?你能憑借直覺崎场,迅速地判斷出誰的概率大,誰的概率小嗎遂蛀?下面就是 26 個這樣的問題谭跨。如果你感興趣...
    cnnjzc閱讀 6,835評論 0 12
  • 一般的抽獎管理功能,基本是在一個獎池中放一堆獎品李滴,分別給它們設置不同的數(shù)量和概率螃宙,在獎品沒有發(fā)完的情況下,概...
    wwking02閱讀 3,840評論 1 4
  • 一般的抽獎管理功能所坯,基本是在一個獎池中放一堆獎品谆扎,分別給它們設置不同的數(shù)量和概率,在獎品沒有發(fā)完的情況下芹助,...
    wwking閱讀 10,163評論 3 16
  • 孝青老師逝世一周年祭奠 敬愛的老師: 您在天國365日里好嗎燕酷?去年的今日您來不及與學生們道別籍凝,順著...
    黃相英閱讀 391評論 2 1
  • 2017.11.23日 星期四 天氣晴 11月23日清晨音頻 各位同學,大家早上好苗缩,今天是我們100天精華內容領讀...
    陽明先生_x閱讀 524評論 0 0