無心間看回一年前(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 100spa
但是接下來寫的實現代碼,如今看來可真是亂七八糟的。後來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);
(完)