PHP几个常用的概率算法

说明:全概率计算为统一设置一个中奖概率;单独概率计算则每个奖品分别可以设置一个中奖概率;
算法一(全概率计算)<br/>
此类中奖概率为所有奖项设置一个总的中奖概率,中奖后再从已有的奖品里面随机抽一个奖品
中奖判断:

1.先按照总体的抽奖概率判断该用户是否中奖<br/>
2.如果中奖的话,则随机从所有奖品里面抽出一个<br/>
3.所有奖品抽完后中奖概率变成0<br/>
如:中奖概率为20%,一等奖1个,二等奖2个,三等奖3个;<br/>

首先判断是否中奖,中奖概率为20%<br/>
中奖的客户再判断中哪个奖项,一等奖的概率:1/(1+2+3);二等奖概率:2/(1+2+3);三等奖概率:3/(1+2+3);<br/>
每次中某个奖品后,相应的奖品数量需要-1,如中出一个三等奖后,相应的中奖概率:一等奖的概率:1/(1+2+2);二等奖概率:2/(1+2+2);三等奖概率:2/(1+2+2);
某个奖品数量抽完后,该奖品中奖概率变成0,如一等奖抽完后,一等奖中奖概率:0/(0+2+3)
全部奖品抽空后,不能再中奖

 /**
 * 抽奖结果,概率计算
 * @param $award   奖品数组
 * @return array|bool
 */
private function getAwardresult($award)
{
         if (empty($award)) {
              return false;
         }
          //总的奖项目
          $i = count($award);
          $prob_sum = 0;
          foreach($award as $item){
            $prob_sum += $item['size'];
          }
          //总的概率
          $award_chance = 20;
          $rand = mt_rand(1, 10000);  //1-10000之间随机取值
          $award_chance *= 100;       //0.01-100.00的概率
          if ($rand <= $award_chance) {
              $level = mt_rand(1, $i); //中了几等奖 1 一号奖品,2 二号奖品,3 三号奖品,4 四号奖品,5 五号奖品
              $num = $award[$i]/$prob_sum;
              if($num>0){
               return $level;
              }
              return false;
          }
          return false;
}

  


算法二(单独概率计算)<br/>

此类营销活动奖品中奖概率可以单独设置,每次仅需要判断是否中奖不需要二次确认中什么奖品
中奖判断:

根据设置的中奖概率,判断该次抽奖是否中奖,中了什么奖品<br/>
如果某奖品已经是小于等于0,那么这个奖品的获得概率合并到不中奖概率<br/>
所有奖品抽完后,不能继续中奖<br/>
如:一等奖中奖概率为1%,奖品10个,二等奖2%,奖品10个,三等奖3%,奖品20个<br/>

只要一二三等奖奖品没抽空的情况下,不管已经抽出去了多少个一二三等奖,抽中一二三等奖的概率都不变,分别为;1%.2%,3%;
如果一等奖已经抽完了,用户还是抽到1-100中的数字,此时用户并没有中一等奖,因为奖品数量是0,所以还是不中奖
假如数组如下:
$award = array(
array('id' => 1, 'prize_name' => '平板电脑', 'prize_num' => 1, 'prize_prob' => 1),
array('id' => 2, 'prize_name' => '数码相机', 'prize_num' => 5, 'prize_prob' => 2),
array('id' => 3, 'prize_name' => '音箱设备', 'prize_num' => 10, 'prize_prob' => 10),
array('id' => 4, 'prize_name' => '4G优盘', 'prize_num' => 12, 'prize_prob' => 10),
array('id' => 5, 'prize_name' => '10Q币', 'prize_num' => 22, 'prize_prob' => 15),
array('id' => 6, 'prize_name' => '下次没准就能中哦', 'prize_num' => 50, 'prize_prob' => 40),
);
/**
* 抽奖结果,概率计算
* @param array $award   奖品数组
* @return array|bool
*/
private function getAwardresult(array $award)
{
    if (empty($award)) {
        return false;
    }
    //初始化几率区间
    $baseNum = 10000;
    $randNum = mt_rand(1, $baseNum);
    //总库存,总概率
    $prizeTotalNum = array_sum(array_column($award, 'prize_num'));
    $prizeTotalProb = array_sum(array_column($award, 'prize_prob'));
    //判断随机数 是否大于 总概率
    if (empty($prizeTotalNum) || empty($prizeTotalProb) || $randNum > $prizeTotalProb * 100) {
        return false;
    }
    $vMax = 0;
    foreach ($award as $key => $v) {
        $vMin = $vMax;
        $vMax += $v['prize_prob'] ? ($v['prize_prob'] / 100) * $baseNum : 0;
        if (empty($v['prize_num']) || $randNum < $vMin || $randNum > $vMax) {
            return false;
        }
        //如果总概率不是100%,就小小的控制一下,这样相当对每一个奖品并发起到了控制
        $key = "shake_lock_level_award_{$v['id']}";
        if ($prizeTotalProb != 10000 && Redis::getInstance()->get($key)) {
            return false;
        }
        Redis::getInstance()->setex($key, 5, 1);
        return [
            'id' => $v['id'],
            'prize_name' => $v['prize_name'],
            'prize_num' => $v['prize_num'],
            'prize_prob' => $v['prize_prob'],
        ];
    }
    return false;
}

算法二是一段比较经典的概率算法,$award是一个预先设置的数组,v表示中奖概率大小是0-100,开始是从,10000这个概率范围内筛选一个数是否在他的出现概率范围之内, 如果在,说明中奖,再在总共的奖项中筛选一个数,看中了几等奖,当然还有去判断处理看看该奖项是否还有剩余
posted @ 2020-03-27 18:47  晴箜万里  阅读(692)  评论(0编辑  收藏  举报