標籤:
無意間看回一年前(2009-2-10)在ppc和houlai、youd討論關於“根據機率取隨機數”演算法。問題是這樣的:
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
可是接下來寫的實現代碼,現在看來可真是亂七八糟的。後來youd提出了一個更好的演算法並寫出了php代碼,讓自己汗顏不已:
youd:開始是從1,1000這個機率範圍內篩選第一個數是否在他的出現機率範圍 之內,如果不在,則將機率空間,也就是k的值減去剛剛的那個數位機率空間,在本例當中就是減去100,也就是說第二個數是在1,900這個範圍內篩選 的。我想應該很容易理解,這樣篩選到最終,總會有一個數滿足要求(比如說前三個都不幸成為了非Luck Num,那麼k已經-100-200-300=400了,那麼最後一個數無論如何也會滿足要求的。相當於拿東西,第一個不是,第二個不是,第三個還不是, 那最後一個一定是。
這個演算法的優點是,對於沒有機率重疊的數字進行篩選,最多隻需要遍覽一次數組就足夠了。程式簡單,效率高
一年後看回此貼,真是感慨自己當初寫PHP代碼的幼稚(當然,現在其實水平也很菜)。趁著現在寫論文的休息時間,把youd的演算法用函數給封裝起來,以待以後使用。
不知道當年一起討論的人,現在如何了......
代碼如下:
<?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);
(完)
:根據機率取隨機數的php演算法