代码说

code is poetry

代码说    
碎碎念:燃烧你的梦,追求着真实、昂首挺胸的你热血沸腾到让人无法接近!  换一换

中奖概率问题

作者:coderzheng 发布于:2015-11-30 11:10 Monday 分类:other  阅读模式

来看看中奖概率问题:
假设目前有5个奖项,分别为一等奖、二等奖、三等奖、四等奖和五等奖。
各奖项对应的中奖概率如下:
一等奖    10%
二等奖    10%
三等奖    10%
四等奖    20%
五等奖    50%
现在要求随机产生一个奖项,满足上述概率。在php中,很容易联想到随机函数mt_rand,mt_rand(m,n)返回一个介于m和n之间的随机数。那要怎么才能让这个随机数和中奖概率对应上呢?最简单的思路是这样:因为mt_rand(m,n)能随机产生一个大于等于m且小于等于n的整数,如果我们将m到n这个段中的数字按照概率分成段,然后将产生的随机数,对应到相应的段中去,就能让随机数和概率产生关联。比如对上面给出的示例:
设置m为1,n为100。使用mt_rand(1,100)会产生一个随机数,判断这个数落在(1,10)、(11,20)、(21,30)、(31,50),(51,100)这五个区间的的哪个区间,就能知道此次中奖项的序号。这里一般人会想不明白,这五个区间为什么要这么定义。似乎每个区间中的起始点是可以灵活变化的,比如按照下面的方式来定义区间:
(1,50) <=> 五等奖
(51,70) <=> 四等奖
(71,80) <=> 三等奖
(81,90) <=> 二等奖
(91,100) <=> 一等奖
此时,我们可以在程序中判断,随机数落在第一个区间时为五等奖,第二个区间时为四等奖,其余的依次类推。我们甚至可以组合不连续的区间,只要在程序中能正确对应到相应的奖项即可。比如这样:
(1,30) + (71,90) <=> 五等奖
(31,50) <=> 四等奖
(51,60) <=> 三等奖
(61,70) <=> 二等奖
(91,100) <=> 一等奖
上面的算法可以正确处理中奖概率之和为100%的问题,如果概率总和不是100%呢?比如这样:
一等奖    0%
二等奖    10%
三等奖    10%
四等奖    20%
五等奖    50%
此时,我们可以人为补齐概率为100%,仍然按照100%的情况进行区间划分。在程序端做出相应的处理即可,比如本例中:如果我们沿用下面的区间划分:(1,10)、(11,20)、(21,30)、(31,50),(51,100),那么,如果随机数在(1,10)区间中,返回一个负数的中奖序号,表示没有中奖即可。
继续延伸问题,如果中奖概率中出现了小数呢?比如:12.5%,30.6%。。。
解决方案是:将小数形式的百分比转化成整数形式。比如上面的12.5%应该转化成125/‰,306/‰。然后,区间段中的最大值取1000即可。
关于中奖概率问题,这里有人给出了其他算法,有兴趣的朋友可以自行研究:
// 传参示例:$proArr = array ( 1 => 10, 2 => 10, 3 => 10, 4 => 20, 5 => 50, ) function getRand($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; 
        } 
    } 
    unset ($proArr); 
    return $result; 
}
这个算法也能正确产生符合条件的中奖序号,但是比本文中的提到的算法效率要低一些(可能需要循环多次),但是优点是不需要自定义区间。只需要传递一个中奖序号和概率的数组即可。


============================== 全文完 ===============================

标签: 算法

你可以发表评论、引用到你的网站或博客,或通过RSS 2.0订阅这个博客的所有文章。
上一篇: 嵌套函数声明与闭包  |  下一篇:无法再见的时光