:根據機率取隨機數的php算法

無心間看回一年前(2009-2-10)在ppc和houlai、youd討論關於「根據機率取隨機數」算法。問題是這樣的:php

houlai:設隨機抽到A的機率爲0.1,B的機率爲0.2,C的機率爲0.3,D的機率爲0.4,如今求按此機率隨機抽出一個字母的算法算法

當時本身剛學了機率論,因此沒有采用網上常見的「利用數組初始化,而後依據機率填充內容,再而後打亂該數組,最後再依據某個規則取數組內某個值」(事實上也會把內存給耗光),而改成另一種方法:數組

horseluke:把1——100當作是一條線段。而後機率就是用於切割這條線條的,咱們要作的其實只須要隨機產生一個數,而後看這個數對應於哪一條分線條就OK了。這樣,數組不需太大,並且還能獲得一樣的效果。函數

舉個例子,A字母爲30%機率,B字母爲70%機率。那麼能夠把這條0——100的線段分割爲2段。
一段爲0——30,爲A字母;其他的分屬B字段
好了,程序如今隨機抽取了30這個數,那麼很明顯,結果就是A了。
測試

|-----------|-----------------------|
0           30                     100
spa

但是接下來寫的實現代碼,如今看來可真是亂七八糟的。後來youd提出了一個更好的算法並寫出了php代碼,讓本身汗顏不已:orm

youd:開始是從1,1000這個機率範圍內篩選第一個數是否在他的出現機率範圍 以內,若是不在,則將機率空間,也就是k的值減去剛剛的那個數字的機率空間,在本例當中就是減去100,也就是說第二個數是在1,900這個範圍內篩選 的。我想應該很容易理解,這樣篩選到最終,總會有一個數知足要求(好比說前三個都不幸成爲了非Luck Num,那麼k已經-100-200-300=400了,那麼最後一個數不管如何也會知足要求的。至關於拿東西,第一個不是,第二個不是,第三個還不是, 那最後一個必定是。內存

這個算法的優勢是,對於沒有機率重疊的數字進行篩選,最多隻須要遍覽一次數組就足夠了。程序簡單,效率高get

一年後看回此貼,真是感慨本身當初寫PHP代碼的幼稚(固然,如今其實水平也很菜)。趁着如今寫論文的休息時間,把youd的算法用函數給包裝起來,以待之後使用。it

不知道當年一塊兒討論的人,如今如何了......

代碼以下:

<?php

function pro_rand( $proArr ){
    $result = '';

    //機率數組的總機率精度
    $proSum = array_sum($proArr);
   
   
    foreach ( $proArr as $key => $proCur ){
        $randNum = mt_rand(1, $proSum);
        if( $randNum <= $proCur ){
            $result = $key;
            break;
        }else{
            $proSum -= $proCur;
        }
    }
   
    return $result;
   
}



function pro_rand_unique_multi( $proArr, $num = 1 ){
    $result = array();
    if( $num > count($proArr) ){
        trigger_error('The stack number of Probability Array is GREATER THAN you set!', 256);
    }
   
    while(1){
        if($num < 1){
            break;
        }
        $curResult = pro_rand($proArr);
        $result[] = $curResult;
        //重置總機率精度,有待機率論驗證
        unset($proArr[$curResult]);
        $num -= 1;
    }
   
    return $result;
   
}

  

以上函數的測試代碼:

pro_rand:

$proArr = array(10,20,30,40);
mt_srand(time());

$distribute = array();
for($k = 1; $k <= 1000; $k++ ){

    $result = pro_rand($proArr);
    if(!isset($distribute[$result])){
        $distribute[$result] = 1;
    }else{
        $distribute[$result] += 1;
    }

}

ksort($distribute);
var_export($distribute);

pro_rand_unique_multi:

$proArr = array(10,20,30,40);
mt_srand(time());

$distribute = array();
for($k = 1; $k <= 1000; $k++ ){

    $result = pro_rand_unique_multi($proArr, 2);
    foreach($result as $key => $proKey){
        if(!isset($distribute[$proKey])){
            $distribute[$proKey] = 1;
        }else{
            $distribute[$proKey] += 1;
        }
    }

}

ksort($distribute);
var_export($distribute);

(完)

相關文章
相關標籤/搜索