首頁  >  文章  >  後端開發  >  PHP抽獎演算法思路?

PHP抽獎演算法思路?

WBOY
WBOY原創
2016-08-04 09:20:241405瀏覽

網上有個爛大街的演算法,具體請看連結

大概原理是:
1,產生一個隨機數;
2,循環對比第N個獎品的機率;
3,決定獎品;

如果按照這個演算法,主觀上是抽一次獎,但客觀上,把每個獎品都循環抽了一遍,其實是抽了N次,不知道我的理解對不對。

下面是我寫的演算法,具體原理是:
1,先確定隨機數(幸運號碼)的範圍;
2,確定每個獎品的號碼區間,確保只抽一次獎能對應到某個獎品;
3,根據範圍產生幸運號碼;
4,根據範圍決定獎品;

請教各路大神,有沒有更優的算法或思路?

<code>$goods = [
            0 => [
                'id'    => 1,
                'name'  => '苹果电脑',
                'odds'  => '0.01' //概率
            ],
            1 => [
                'id'    => 2,
                'name'  => 'Iphone',
                'odds'  => '10.99'
            ],
            2 => [
                'id'    => 3,
                'name'  => '200元红包',
                'odds'  => '19'
            ],
            3 => [
                'id'    => 4,
                'name'  => '安慰奖',
                'odds'  => '30'
            ],
            4 => [
                'id'    => 5,
                'name'  => '什么都没抽到',
                'odds'  => '40'
            ],
        ];


        $baseOdds = 100; //抽奖概率基数,默认100%
        $maxBase = 1; //抽奖概率基数倍数,默认1,如果奖品概率有小数位,该倍数为10的小数位数平方,具体看下面

        foreach ($goods as $good) {
            $decimal = strpbrk($good['odds'], '.'); //获取小数点后面的内容
            if ($decimal !== false) {
                $decimalCount = strlen($decimal) - 1;//获取小数点后面的位数
                $newMaxBase = pow(10, $decimalCount); //例如概率如果是0.01,则全局的抽奖概率基数需要以10的平方倍数上涨
                if ($newMaxBase > $maxBase) {
                    $maxBase = $newMaxBase; //更新基数倍数
                }
            }
        }
        $baseOdds = $maxBase ? $baseOdds * $maxBase : $baseOdds; //更新概率基数

        $start = 1;
        $end = 0;
        $luckyCompare = $tickets = [];
        //为每个奖品生成一个幸运数区间
        foreach ($goods as $key => $good) {
            $newOdds = $good['odds'] * $maxBase;
            $end = $end + $newOdds;
            $luckyCompare[$good['id']] = [$start, $end];
            $tick = mt_rand($start, $end);
            $start = $start + $newOdds;
            $tickets[$good['id']] = $tick;
        }
        $luckyNumber = mt_rand(1, $baseOdds);
        var_dump($luckyNumber);
        var_dump($luckyCompare);
        foreach ($luckyCompare as $goodId => $compare) {
            if ($compare[0] <= $luckyNumber && $compare[1] >= $luckyNumber) {
                $luckyGood = $goodId; //最终的奖品
                break;
            }
        }

        var_dump($luckyGood);</code>

回覆內容:

網路上有個爛大街的演算法,具體請看連結

大概原理是:
1,產生一個隨機數;
2,循環對比第N個獎品的機率;
3,決定獎品;

如果依照這個演算法,主觀上是抽一次獎,但客觀上,把每個獎品都循環抽了一遍,其實是抽了N次,不知道我的理解對不對。

下面是我寫的演算法,具體原理是:
1,先確定隨機數(幸運號碼)的範圍;
2,確定每個獎品的號碼區間,確保只抽一次獎能對應到某個獎品;
3,根據範圍產生幸運號碼;
4,根據範圍決定獎品;

請教各路大神,有沒有更優的算法或思路?

<code>$goods = [
            0 => [
                'id'    => 1,
                'name'  => '苹果电脑',
                'odds'  => '0.01' //概率
            ],
            1 => [
                'id'    => 2,
                'name'  => 'Iphone',
                'odds'  => '10.99'
            ],
            2 => [
                'id'    => 3,
                'name'  => '200元红包',
                'odds'  => '19'
            ],
            3 => [
                'id'    => 4,
                'name'  => '安慰奖',
                'odds'  => '30'
            ],
            4 => [
                'id'    => 5,
                'name'  => '什么都没抽到',
                'odds'  => '40'
            ],
        ];


        $baseOdds = 100; //抽奖概率基数,默认100%
        $maxBase = 1; //抽奖概率基数倍数,默认1,如果奖品概率有小数位,该倍数为10的小数位数平方,具体看下面

        foreach ($goods as $good) {
            $decimal = strpbrk($good['odds'], '.'); //获取小数点后面的内容
            if ($decimal !== false) {
                $decimalCount = strlen($decimal) - 1;//获取小数点后面的位数
                $newMaxBase = pow(10, $decimalCount); //例如概率如果是0.01,则全局的抽奖概率基数需要以10的平方倍数上涨
                if ($newMaxBase > $maxBase) {
                    $maxBase = $newMaxBase; //更新基数倍数
                }
            }
        }
        $baseOdds = $maxBase ? $baseOdds * $maxBase : $baseOdds; //更新概率基数

        $start = 1;
        $end = 0;
        $luckyCompare = $tickets = [];
        //为每个奖品生成一个幸运数区间
        foreach ($goods as $key => $good) {
            $newOdds = $good['odds'] * $maxBase;
            $end = $end + $newOdds;
            $luckyCompare[$good['id']] = [$start, $end];
            $tick = mt_rand($start, $end);
            $start = $start + $newOdds;
            $tickets[$good['id']] = $tick;
        }
        $luckyNumber = mt_rand(1, $baseOdds);
        var_dump($luckyNumber);
        var_dump($luckyCompare);
        foreach ($luckyCompare as $goodId => $compare) {
            if ($compare[0] <= $luckyNumber && $compare[1] >= $luckyNumber) {
                $luckyGood = $goodId; //最终的奖品
                break;
            }
        }

        var_dump($luckyGood);</code>

只能說 呵呵 了。抽獎的演算法的決定權不在程式設計師,這才是重點。
通常抽獎活動是由行銷部門發起、策劃、評估、運作的,程式設計師的角色只是做實現而已。該行銷行為策劃案應該包含獎品設定、活動時間、推廣策略、冷/熱場時間、獎品投放批次等等東西,簡單來說,這是行銷部門導演的一場戲,開發人員是現場指揮,程式負責調度,抽獎者就是演員了。
所以,抽獎演算法根本沒用,只要你放出去的獎品不要超出獎金池就行了,別的都不重要。

可能有人會說了,那怎麼體現抽獎的公平公正?其實幾乎不需要考慮這個問題,因為程式寫好之後是自動執行的,這個時候對所有參與者來說已經是一個同等環境了,不存在不公平的問題。
典型的偽演算法如下:
雨露獎:消費抵扣券5元,中獎率100%;
水坑獎:消費抵扣券10元,中獎率50%;
奔雷獎:消費抵扣券100元,中獎率5%;
雷劈獎:iPad一個,共6個。
活動時間12小時,彩券無上限。
演算法:抽獎時間戳微秒部分是20的倍數=奔雷,是單數=水坑,否則為雨露。每兩個小時為一個週期,下一週期開始時的第一個抽獎人獲得上一個週期的雷劈獎,整個活動在持續時間截止後收到第一個彩票則完全結束。

有不公平的問題嗎?所以,呵呵。最後還是要根據需求寫程式。

<code>$arr = array(10000,1000,100,10,1);
for ($i=0;$i<5;$i++){
    
    if(rand(1,$arr[$i])==1){
        
        echo "恭喜你中了".$i."奖";//这个描述自己写
    }
    
}</code>
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn