生成有偏随机数据

问题:

你想生成随机数字,但又希望这些数字具有某种倾向性,也就是说这些数字出现在某个范围内的概率会比出现在其他范围内的概率更高。例如,你想要根据每个广告活动未展示的剩余数目成比例地推出一系列网页横幅广告。

方案:

使用如例下所示的pc_rand_weighted()函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php  
/* 大致思想就是:将剩余展示数目累加
比如:A:5,B:8,C:10
累加后:A:5,B:13,C:23
然后随即一个数,范围0~23
判断 随机数是否小于A或B或C,小于则成功

效果:谁的剩余展示数目越多,被随机到的几率越高
*/
function pc_rand_weighted($numbers)
{
$total = 0;
foreach ($numbers as $number => $weight) {
$total += $weight;
$distribution[$number] = $total;
}
$rand = mt_rand(0, $total - 1);
foreach ($distribution as $number => $weights) {
if ($rand < $weights) {
return $number;
}
}
}
?>

讨论

想像一下,如果不使用一个期值为剩余展示数目的数组,而是用一个广告数组,其中每个广告的实际展示次数都与该广告的剩余展示数目一样多。你可以简单地在数组中选取一个未加权的随机位置,并从该位置开始展示。

如果广告的剩余展示数目达到百万级的话,使用这种技术则会消耗大量的内存。相反,你可以计算相应数组的大小(通过对剩余展示数目求合计算),从这个伪数组的元素总数中选取一个随机的数字,然后遍历该数组并找出与所选数字对应的广告。例如:

1
2
3
4
5
6
7
<?php  
$ads = array('ford' => 12234, //广告客户,剩余展示数目
'att' => 33434,
'ibm' => 16823
);
$ad = pc_rand_weighted($ads);
?>