Setting Sun's Blog – 残阳 彪悍的人生不需要解释~~

17十二/090

使用 PHP 解决机率和权重问题

在程序设计中我们经常会碰到要解决机率或权重相关的问题,例如经常碰到的中奖问题。 有一个百货公司办了一个抽奖活动,一等奖的中奖率是3%,二等奖是5%,三等奖是10%,纪念奖是20%。 处理这个需求,一般的程序员会采用如下的逻辑:

$randKey = mt_rand(1, 100);
$prizeLevel = 0;

if ($randKey <= 3)
{
	$prizeLevel = 1;
}
elseif ($randKey <= 3 + 5)
{
	$prizeLevel = 2;
}
elseif ($randKey <= 3 + 5 + 10)
{
	$prizeLevel = 3;
}
elseif ($randKey <= 3 + 5 + 10 + 20)
{
	$prizeLevel = 4;
}

这段程序似乎有些复杂,我们可以简化一下它:

$prizeInfo = array(
	0 => 62,
	1 => 3,
	2 => 5,
	3 => 10,
	4 => 20
);

$randKey = mt_rand(1, 100);
$radix = 0;

foreach ($prizeInfo as $prizeLevel => $prizeOdds)
{
	$radix += $prizeOdds;

	if ($randKey <= $radix)
	{
		break;
	}
}

权重和机率类似,只是概率总值不固定,我们只想直观的用一个数字的大小来表示想给它多大的机会。 我们修改一下需求,一等奖的权重是5,二等奖是10,三等奖是20,纪念奖是50,没有奖励是100。 实际上,稍微修改一下逻辑,我们就可以处理这个问题了:

$prizeInfo = array(
	0 => 100,
	1 => 5,
	2 => 10,
	3 => 20,
	4 => 50
);

$randKey = mt_rand(1, array_sum($prizeInfo));
$radix = 0;

foreach ($prizeInfo as $prizeLevel => $prizeOdds)
{
	$radix += $prizeOdds;

	if ($randKey <= $radix)
	{
		break;
	}
}

但是实际上,我们在处理更复杂的需求时会发现,即使我们使用了 PHP 中号称更好用线性分布更平均的 mt_rand,统计结果看起来,也不是那么随机,或许这是源于 PHP 本身的问题。 我在设计杀死怪物时按照权重掉落物品的逻辑时,就遇到了这样的问题。 稍作处理后,结果稍微满意了些,基本上,我只是扩大了随机范围,扰乱了权重的顺序。

$prizeInfo = array(
	0 => 100,
	1 => 5,
	2 => 10,
	3 => 20,
	4 => 50
);

$prizeLevels = array_keys($prizeInfo);
shuffle($prizeLevels);

$randKey = rand(1, array_sum($prizeInfo));
$radix = 0;

foreach ($prizeLevels as $prizeLevel)
{
	$radix += $prizeInfo[$prizeLevel];

	if ($randKey <= $radix)
	{
		break;
	}
}

这段逻辑基本上可以应付大多数的权重和机率问题! 当然,如果你有更复杂更精确的需求,你可能需要自己设计一个特别的算法。

转自:http://zivn.me/archives/58