1. 程式人生 > >不同中獎概率的多獎包抽獎幾種演算法

不同中獎概率的多獎包抽獎幾種演算法

需求描述:總共有很多個獎包,每個獎包的中獎概率是人為自由設定的,規定每次抽獎必須抽中。
演算法分類:
一、最初自行編寫的演算法:

<?php
//目標id:隨機抽中的獎包id
$pid = 0;
//$pList為獎包資訊存放陣列,是從資料庫中提取出來的
//為方便研究,這裡直接給$pList賦值
$pList = array
(
    [0] => array
        (
            ['id'] => 65,
            ['percent'] => 10
        ),
    [1] => array
        (
            ['id'
] => 64, ['percent'] => 50 ), [2] => array ( ['id'] => 63, ['percent'] => 20 ), [3] => array ( ['id'] => 62, ['percent'] => 80 ), [4] => array ( ['id'
] => 61, ['percent'] => 30 ) ); //中獎概率之總和 $sum = 0; //以中獎概率總和為上限,以1為起始鍵名(1為自增值),以表的id為鍵值的陣列 //ps:本想直接給$randomArr賦值,上面的程式碼也省略,但這樣不利於理解和對比 $randomArr = array(); if(!empty($pList)){ foreach($pList as $key=>$value){ $newSum = $sum + $value['percent']; for($i=$sum
+1;$i<=$newSum;$i++){ //給$randomArr賦值,中獎概率為鍵名,id為鍵值 $randomArr[$i] = $value['id']; } $sum = $newSum; } } //隨機出的數,作為抽中的$randomArr陣列的鍵名 $random = 0; if($sum > 0){ $random = mt_rand(1,$sum); } //確定隨機抽中的獎包id if($random > 0){ $pid = $randomArr[$random]; }

思路:用中獎概率為鍵名,獎包id為鍵值組建陣列 => 隨機鍵名(中獎概率) => 根據鍵名確定鍵值(獎包id)
總結:優點是利於理解,缺點是效率不高

二、經領導指點改進後的演算法:

<?php
//目標id:隨機抽中的獎包id
$pid = 0;
//$pList為獎包資訊存放陣列,是從資料庫中提取出來的
//為方便研究,這裡直接給$pList賦值
$pList = array
(
    [0] => array
        (
            ['id'] => 65,
            ['percent'] => 10
        ),
    [1] => array
        (
            ['id'] => 64,
            ['percent'] => 50
        ),
    [2] => array
        (
            ['id'] => 63,
            ['percent'] => 20
        ),
    [3] => array
        (
            ['id'] => 62,
            ['percent'] => 80
        ),
    [4] => array
        (
            ['id'] => 61,
            ['percent'] => 30
        )
);
//中獎概率之總和
$sum = 0;
//中獎概率存放陣列,以id為鍵名,以中獎概率的累加和為鍵值
//賦值後的$randomArr,形如$randomArr = array(0=>0,65=>10,64=>50,...,61=>30);
$randomArr = array();
//為了方便下面隨機數範圍的比較而設定
$randomArr[0] = 0;
if(!empty($pList)){
    foreach($pList as $key=>$value){
        $sum = $sum + $value['percent'];
        $randomArr[$value['id']] = $sum;
    }
}
//隨機出的概率數
$random = 0;
if($sum > 0){
    $random = mt_rand(1,$sum);
}
//每次迴圈參加比較的上一個key的值
$oldKey = 0;
foreach($randomArr as $key=>$value){
    if($key > 0){
        //根據隨機數大小範圍比較,確定隨機出的獎包id
        if($random>$randomArr[$oldKey] && $random<=$randomArr[$key]){
            $pid = $key;
        }
    }
    $oldKey = $key;
}

思路:用獎包id為鍵名,中獎概率累加和為鍵值組建陣列 => 隨機鍵值(中獎概率) => 判斷中獎概率的範圍 => 根據範圍確定鍵名(獎包id)
總結:優點是易於理解,且執行效率高,只是前期演算法設計時需多動腦筋

三、網路搜來的經典演算法:

<?php
//目標id:隨機抽中的獎包id
$pid = 0;
//$pList為獎包資訊存放陣列,是從資料庫中提取出來的
//為方便研究,這裡直接給$pList賦值
$pList = array
(
    [0] => array
        (
            ['id'] => 65,
            ['percent'] => 10
        ),
    [1] => array
        (
            ['id'] => 64,
            ['percent'] => 50
        ),
    [2] => array
        (
            ['id'] => 63,
            ['percent'] => 20
        ),
    [3] => array
        (
            ['id'] => 62,
            ['percent'] => 80
        ),
    [4] => array
        (
            ['id'] => 61,
            ['percent'] => 30
        )
);
//中獎概率之總和
$sum = 0;
if(!empty($pList)){
    //求$sum的值
    foreach($pList as $key=>$value){
        $sum = $sum + $value['percent'];
    }
    //根據數學概率論的“黑箱摸球”原理設計、取得抽中的獎包id
    if($sum > 0){
        foreach($pList as $key=>$value){
            //以剩餘迴圈概率和為基數,生成隨機數
            $random = mt_rand(1,$sum);
            //若生成的隨機數在本次迴圈的中獎概率範圍內,則抽中本獎包,迴圈終止
            if($random <= $value['percent']){
                $pid = $value['id'];
                break;
            }else{
                //若迴圈未終止,則每迴圈一次,$sum需要減去本次迴圈的中獎概率
                $sum -= $value['percent'];
            }
        }
    }
}

思路:參考數學概率論的“黑箱摸球”
總結: 前人實踐證實可用且執行效率高,只是概率論學得不好的童鞋理解起來較困難

領悟:好的演算法不止要易於理解,還要兼顧執行效率、系統安全和穩健等因素,所以程式設計之前要開動腦筋、利用手頭資源多準備幾種演算法,綜合考慮比較後再做決定