之前有过这样的需求,抽奖活动,奖品按一定权重随机抽取,例如:

奖品 a b c d e
权重 12 32 32 12 12

之前的做法是把每个奖品按照权重,重复加进一个数据,即:

1
[a,a,a,a...b,b,b,b,b...c,c,c,c,c...d,d,d,d...e,e,e...]

里面有12个a,32个b以此类推。然后用 array_rand() 选出一个。

这种方法首先会的到一个很大的数组,然后循环添加奖品也很费时。今天重新思考这个问题,可以用下面的方法:

  1. 将所有权重相加得到总和
  2. 以总和为最大值得到一个随机数
  3. 循环查找随机数在数组里的区间,进而得到结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$gift = ['a'=>12,'b'=>32,'c'=>32,'d'=>12,'e'=>12];
$sum = array_sum($gift);
$rand = mt_rand(1,$sum);
$i=0;
$the_gift='';
foreach($gift as $key=>$weight){
$i += $weight;
if($rand < $i){
$the_gift = $key;
break;
}
}
var_dump($the_gift);

虽然这个算法里也有循环,但是通常礼物不会很多,计算量非常小。

最后给一个用 map 实现的方法,缺点是要用权重作为key,所以每个权重不能相同。https://www.cnblogs.com/exmyth/p/7100749.html