一個(gè)簡(jiǎn)單抽獎(jiǎng)算法的實(shí)現(xiàn)

最近的一個(gè)業(yè)務(wù)需求是開發(fā)一個(gè)抽獎(jiǎng)管理功能,要求在一個(gè)獎(jiǎng)池中放一堆獎(jiǎng)品,分別給它們?cè)O(shè)置不同的數(shù)量和概率,在獎(jiǎng)品沒有發(fā)完的情況下,概率高的被抽中的幾率就大,反之則低。另外,概率為0的不能被抽中,概率為100則一定要被抽中。

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

實(shí)現(xiàn)抽獎(jiǎng)的方法應(yīng)該有很多,沒有仔細(xì)去考察和搜索那些非常復(fù)雜的算法,這里僅做了一個(gè)簡(jiǎn)單的假設(shè),并在此基礎(chǔ)上推出后面所有的控制邏輯。

  1. 基本假設(shè)

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

暫且不去從數(shù)學(xué)上論證這個(gè)假設(shè)是否真的成立,我們僅從直觀上來(lái)看,R是隨機(jī)的,它的值不論是多少,取到1-100之間任意一個(gè)整數(shù)的概率都是一樣的。

但C越小,R落入0-C之間的概率也會(huì)越小,所以我們大致上可以用C來(lái)控制某個(gè)獎(jiǎng)品的概率。Good!

  1. 實(shí)現(xiàn)邏輯

有了上面的假設(shè),我們就可以考慮實(shí)現(xiàn)一個(gè)獎(jiǎng)池內(nèi)不同獎(jiǎng)品配置情況下的控制邏輯。

首先我們把獎(jiǎng)池中的獎(jiǎng)品做一個(gè)過濾,剔除掉那些不滿足條件的獎(jiǎng)品,比如概率為0的、已經(jīng)發(fā)完的等等。剩下的獎(jiǎng)品都是可以被抽中的,只是概率大小不同而已。

OK,下面我們來(lái)設(shè)置一些概念,以方便后面的行文。

假設(shè)有N個(gè)獎(jiǎng)品,它們都以100為滿概率,那么它們總共的概率空間為O=N*100;
如果這N個(gè)獎(jiǎng)品的概率分別為C1,C2,C3...Cn,那么他們總共的中獎(jiǎng)概率空間就是H=C1+C2+C3+...+Cn,因?yàn)镃n總是小于等于100,所以
H總是小于等于O。

我們把以上這些參數(shù)在后臺(tái)配置好,當(dāng)抽獎(jiǎng)行為發(fā)生時(shí),我們讓系統(tǒng)生成一個(gè)隨機(jī)數(shù)R,1<=R<=O,那么當(dāng)R<=C時(shí),我們就認(rèn)為中獎(jiǎng)了,否則就不中獎(jiǎng)。Good10!

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

例如有a和b兩個(gè)獎(jiǎng)品,概率分別為20和30,那么a的概率空間為20,中獎(jiǎng)區(qū)間為1-20;而b的概率空間為30,但它的中獎(jiǎng)區(qū)間是20-50,這樣D就是(1,20,50)。

然后我們?cè)侔袲從小到大排序并循環(huán),當(dāng)R小于20時(shí),我們認(rèn)為a被抽中;R小于50時(shí),認(rèn)為b被抽中。Good11!

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

因?yàn)槲覀冊(cè)O(shè)想的情況是,不僅某種獎(jiǎng)品概率調(diào)大時(shí)其抽中的幾率增大,而且所有獎(jiǎng)品的概率都調(diào)大時(shí),它們被抽中的幾率都增大。

如果直接把R跟獎(jiǎng)品各自的概率比較,根據(jù)我們上面的邏輯,它們總的中獎(jiǎng)空間H=2×100=200,只要R的值小于200,我們就已經(jīng)認(rèn)為中獎(jiǎng)了;但是當(dāng)a和b兩種獎(jiǎng)品的概率為99時(shí),只有當(dāng)R小于100時(shí)它們才會(huì)被抽中,R落在100到200之間將不被認(rèn)為中獎(jiǎng),這顯然是不對(duì)的。

搞清楚了上面的邏輯,剩下的就是處理一些特殊情況了。
比如,如果某些獎(jiǎng)品的概率為100,這就是我們之前說(shuō)的存在滿概率獎(jiǎng)品。按我們的設(shè)想,當(dāng)有百分百中獎(jiǎng)的獎(jiǎng)品時(shí),我們一定要這種獎(jiǎng)品被抽中。

處理這個(gè)問題,我的方法是把獎(jiǎng)品按概率分成兩組,一組是滿概率獎(jiǎng)品,一組是非滿概率獎(jiǎng)品。當(dāng)滿概率獎(jiǎng)品組不為空時(shí),從中隨機(jī)取出一個(gè)作為被抽中的獎(jiǎng)品放出,直到這些獎(jiǎng)品被抽完。

到此為止整個(gè)邏輯基本結(jié)束,可以著手寫代碼了。Good101!

/**
* 抽獎(jiǎng)核心算法
* @param prize array,所有概率不為0且剩余數(shù)大于0的獎(jiǎng)品數(shù)組 
* @return array 單個(gè)獎(jiǎng)品
* version 2015.12.21
* author thinkmad@sina.com
*/
const FULL_CHANCE = 100;
function calcPrize($prize){
    
    if(!$prize){
        return false;
    }
    
    $arr_chance = array();//所有獎(jiǎng)品概率
    $arr_delimiter = array();//中獎(jiǎng)區(qū)間分界數(shù)組
    $full_chance_prize = $nofull_chance_prize = array();//劃分滿概率和非滿概率數(shù)組
    $H = 0;//中獎(jiǎng)概率空間
    
    //劃分滿概率和非滿概率獎(jiǎng)品
    foreach($prize as $item){
        if($item['prizeChance'] >= self::FULL_CHANCE) {
            $full_chance_prize[] = $item;
        }else{
            $nofull_chance_prize[$item['prizeID']] = $item;
        }
    }
    
    //存在滿概率獎(jiǎng)品,則隨機(jī)取出一個(gè)獎(jiǎng)品并返回
    $len = count($full_chance_prize);
    if($len > 0){
        $r = mt_rand(0,$len-1);
        return $full_chance_prize[$r];
    }
    
    //計(jì)算總概率空間O
    $O = count($prize) * self::FULL_CHANCE;
    
    //計(jì)算總中獎(jiǎng)空間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不在中獎(jiǎng)空間
        return false;
    }else{//R落在中獎(jiǎng)空間
        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,計(jì)算數(shù)組中i起n個(gè)數(shù)的和
* @params $input array,要計(jì)算的數(shù)組
* @params $start,int,起始位置
* @params $num,int,個(gè)數(shù)
* @return int
*/
function array_isum($input,$start,$num){
    $temp = array_slice($input, $start,$num);
    return array_sum($temp);
}

上面是用PHP實(shí)現(xiàn)的一個(gè)核心算法,其中定義了一個(gè)常量FULL_CHANCE為100。還定義了一個(gè)輔助函數(shù)array_isum,用來(lái)計(jì)算一個(gè)數(shù)組中從下標(biāo)i開始n個(gè)數(shù)的和。

這里面其實(shí)還有一個(gè)小小的問題,就是當(dāng)幾種獎(jiǎng)品都是非滿概率獎(jiǎng)品且它們的概率相同,我們需要隨機(jī)抽出一個(gè)作為獎(jiǎng)品放出,如果不做這個(gè)處理,則會(huì)按照默認(rèn)順序先把前面的獎(jiǎng)品發(fā)完再發(fā)后面的。
Enjoy It!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 你的數(shù)學(xué)直覺怎么樣?你能憑借直覺,迅速地判斷出誰(shuí)的概率大,誰(shuí)的概率小嗎?下面就是 26 個(gè)這樣的問題。如果你感興趣...
    cnnjzc閱讀 7,027評(píng)論 0 12
  • 一般的抽獎(jiǎng)管理功能,基本是在一個(gè)獎(jiǎng)池中放一堆獎(jiǎng)品,分別給它們?cè)O(shè)置不同的數(shù)量和概率,在獎(jiǎng)品沒有發(fā)完的情況下,概...
    wwking02閱讀 3,951評(píng)論 1 4
  • 一般的抽獎(jiǎng)管理功能,基本是在一個(gè)獎(jiǎng)池中放一堆獎(jiǎng)品,分別給它們?cè)O(shè)置不同的數(shù)量和概率,在獎(jiǎng)品沒有發(fā)完的情況下,...
    wwking閱讀 10,310評(píng)論 3 16
  • 孝青老師逝世一周年祭奠 敬愛的老師: 您在天國(guó)365日里好嗎?去年的今日您來(lái)不及與學(xué)生們道別,順著...
    黃相英閱讀 407評(píng)論 2 1
  • 2017.11.23日 星期四 天氣晴 11月23日清晨音頻 各位同學(xué),大家早上好,今天是我們100天精華內(nèi)容領(lǐng)讀...
    陽(yáng)明AGI閱讀 583評(píng)論 0 0