无意间看回。问题是这样的:
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的算法用函数给包装起来,以待以后使用。
不知道当年一起讨论的人,现在如何了......
代码如下:
<?phpfunction 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);
(完)