当前位置:Gxlcms > PHP教程 > PHP抽奖算法思路?

PHP抽奖算法思路?

时间:2021-07-01 10:21:17 帮助过:17人阅读

网上有个烂大街的算法,具体请看链接

大概原理是:
1,生成一个随机数;
2,循环对比第N个奖品的概率;
3,确定奖品;

如果按照这个算法,主观上是抽一次奖,但客观上,把每个奖品都循环抽了一遍,其实是抽了N次,不知道我的理解对不对。

下面是我写的算法,具体原理是:
1,先确定随机数(幸运号码)的范围;
2,确定每个奖品的号码区间,确保只抽一次奖能对应到某个奖品;
3,根据范围生成一个幸运号码;
4,根据范围确定奖品;

请教各路大神,有没有更优的算法或思路?

$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);

回复内容:

网上有个烂大街的算法,具体请看链接

大概原理是:
1,生成一个随机数;
2,循环对比第N个奖品的概率;
3,确定奖品;

如果按照这个算法,主观上是抽一次奖,但客观上,把每个奖品都循环抽了一遍,其实是抽了N次,不知道我的理解对不对。

下面是我写的算法,具体原理是:
1,先确定随机数(幸运号码)的范围;
2,确定每个奖品的号码区间,确保只抽一次奖能对应到某个奖品;
3,根据范围生成一个幸运号码;
4,根据范围确定奖品;

请教各路大神,有没有更优的算法或思路?

$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);

只能说 呵呵 了。抽奖的算法的决定权不在程序员,这才是重点。
通常抽奖活动是由营销部门发起、策划、评估、运作的,程序员的作用只是做实现而已。该营销行为策划案应该包含奖品设置、活动时长、推广策略、冷/热场时间、奖品投放批次等等东西,简单来说,这是营销部门导演的一场戏,开发人员是现场指挥,程序负责调度,抽奖者就是演员了。
所以,抽奖算法根本没用,只要你放出去的奖品不要超出奖池就行了,别的都不重要。

可能有人会说了,那怎么体现抽奖的公平公正?其实几乎不需要考虑这个问题,因为程序写好之后是自动执行的,这个时候对所有参与者来说已经是一个同等环境了,不存在不公平的问题。
典型的伪算法如下:
雨露奖:消费抵扣券5元,中奖率100%;
水坑奖:消费抵扣券10元,中奖率50%;
奔雷奖:消费抵扣券100元,中奖率5%;
雷劈奖:iPad一个,共6个。
活动时间12小时,彩票无上限。
算法:抽奖时间戳微秒部分是20的倍数=奔雷,是单数=水坑,否则为雨露。每两个小时为一个周期,下一周期开始时的第一个抽奖人获得上一个周期的雷劈奖,整个活动在持续时间截止后收到第一个彩票则完全结束。

有不公平的问题吗?所以,呵呵。最后还是要根据需求写程序。

$arr = array(10000,1000,100,10,1);
for ($i=0;$i<5;$i++){
    
    if(rand(1,$arr[$i])==1){
        
        echo "恭喜你中了".$i."奖";//这个描述自己写
    }
    
}

人气教程排行