令人纠结的php几率算法问题06/24/2010
唉!现在终于发现上学时不好好念书有多少的坏处了,概率几率对于我来说一直是一个很难弄清楚的问题。今天,我又继续让这个问题纠结上了。好吧!来说说我的那点事儿,首先注明一下:这是一篇求助性文字,我的几率算法也许根本就是不对的。如果恰巧有数学系专家学者路过,那希望您能留言说两句。问题是这样子的:在一个物品合成系统中,需要使用几件不同的物品来合成宝石,有不同的几率生成一颗(50%)、两颗(16%)和三颗(2%)宝石,其余的为爆掉,即什么也得不到。
我的PHP代码实现是这样子的:
<?php //初始化数组 $stone_arr = array( array( 'num' => 1, 'prob' => '50%' ), array( 'num' => 2, 'prob' => '16%' ), array( 'num' => 3, 'prob' => '2%' ) ); //随机获得一个幸运数字 $luck_num = mt_rand( 0, 99 ); //初始化几率区间和最终宝石生产数目 $lucky_range = $made_num = 0; foreach( $stone_arr as $sa ){ $prob = intval( $sa['prob'] ); if( $luck_num >= $lucky_range && $luck_num < $lucky_range + $prob ){ $made_num = $sa['num']; break; } else{ $lucky_range += $prob; } } for( $i = 0; $i < $made_num; $i++ ){ //生产宝石的逻辑 } ?>
左思右想,我总觉得这个并没有问题,可我的同事坚持认为$luck_num = mt_rand( 0, 99 )这一行应该放到循环体的里面,说的也有道理呀!不过我觉得这样是不是多给了玩家机会?(当然,从玩家的角度来说,机会越多越好。呵呵~),截至记者发稿时止,我还是没有弄清楚是不是该把随机数放到循环里面?或者这两种方法都不对?麻烦路过的明眼人给俺说道说道,不甚感激

你贴的这个是对的,放进循环内是错误的。
这段代码本质上是实现了阶梯表查找,即先生成了一个随机数,随后通过逐区间查找的方式确定这个随机数落在哪个区间内。
从逻辑上判断很容易,单次合成是“一个”动作,并不涉及多步逻辑,显然只生成一次随机数才是合理的。
把随机数生成挪到循环内的错误性很容易证明,举一个反例就可以了。考虑这样一种生成概率:60%机会生成1颗,40%机会生成2颗。那么显然,这样一种生产概率下,合成有100%的几率得到宝石,只是数量的问题而已。假如随机数生成是在循环内,却有可能每一轮区间比较均失败,最后什么都没有。例如,在第一次执行循环体时,生成了70,于是第一次循环比较失败(需要的是在0与60之间),进入第二次循环,而这时候又运起不好随机生成了30,于是第二次又比较失败,最后的结果是0个宝石。这个案例就可以作为反例证明其错误性。
在你这个程序中,假如其他不变动,只把随机数生成挪到循环内部,那么实际概率也是可以计算的:
1颗: 50%
2颗: (1-50%) * 16% = 8%
3颗:(1-50%) * (1-16%) * 2% = 0.84%
无产品:(1-50%) * (1-16%) * (1-2%) = 41.16%
其实如果只是单纯要比较2个方案,最简单的方案就是让他们分别跑上几万次,统计结果概率,和需求概率做一下比较马上就可以看出来了。
ps. 博主是福州人?我们还是老乡耶……
啊!太感谢你能给出如此详尽的回复,受教了。谢谢!概率这个东西着实一直困扰的我头疼,看了您的解答,我又明白了一些,还得修炼。。。呵呵!我算半个福州人吧,现在福州工作
补充:正文中的代码其实有一个bug,我以上的分析是无视了这个bug的。
就是这行: $luck_num = mt_rand( 0, 100 );
PHP中的mt_rand后面的参数指定的是一个闭区间,也就是说mt_rand( 0, 100 )实际上可能结果是101个数(包括了0和100),因此按照需求,这里应该写mt_rand( 0, 99 )才是正确的。
谢谢!确实是我写错了,我马上改过来
放外面吧?放里面几率变大了…
汗,我是福州人,但是现在不在福州工作>_<
回ray:
计算表明,放里面实际上是概率变小了……
看不懂…
我的做法是:
<?php
$a = array_fill(0,50, 1);
$b = array_fill(0,16, 2);
$c = array_fill(0,2, 3);
$d = array_fill(0,32, 0);
$arr = array_merge($a, $b, $c);
//var_dump($arr);
$d = mt_rand(0,99);
echo $arr[$d];
顺便说一下 我也是福州的 在福州工作
这个东西实际上很简单,不需要考虑那么深:
function a(){
$n = mt_rand(0,99);
$result = 0;
if($n >= 0 && $n = 0 && $n = 2 && $n = 50 && $n <= 99){
$result = 1;
}
return $result;
}
function a(){
$n = mt_rand(0,99);
$result = 0;
if($n >= 0 && $n = 1){
$result = 1;
}
if($n >=2 && $n =50 && $n <= 99){
$result = 2;
}
return $result;
}
function a(){
$n = mt_rand(0,99);
$result = 0;
if($n >= 0 && $n = 1){
$result = 1;
}
if($n >=2 && $n = 50 && $n <=99){
$result = 3;
}
return $result;
}
晕,这个页面程序有BUG,我写程序到上面都帮我改了!
博主福州那上班?
呵呵,为什么你想知道这个呢?
看楼主的zendstudio设置xdebug,分享一下我的一段概率执行代码:
http://www.hemono.com/?p=268
1. /**
2. * 以$x/$y的概率返回true
3. *
4. * @param int $x
5. * @param int $y
6. * @return bool
7. *
8. * prob(1,2) = 1/2
9. * prob(1,100) = 1/100
10. *
11. */
12. function prob($x, $y){
13. return ( mt_rand(0, $y-1) < $x );
14. }
博主,你的flash录屏工具叫什么名字?
嘿嘿!看我的录像做的帅,就坐不住了吧?嗯!他的名字叫做demo builder.
以前做过一年的php培训,我也经常录像的:)