zoukankan      html  css  js  c++  java
  • 随机数算法根据概率取随机数

    第一种:数组填充

    第二种:线段法

    第三种:没看懂PHP,希望网友能赐教。。。

    houlai:设随机抽到A的概率为0.1,B的概率为0.2,C的概率为0.3,D的概率为0.4,现在求按此概率随机抽出一个字母的算法

    当时自己刚学了概率论,所以没有采用网上常见的“利用数组初始化,然后依据概率填充内容,再然后打乱该数组,最后再依据某个规则取数组内某个值”(事实上也会把内存给耗光),而改为另外一种方法:

    horseluke:把1——100看成是一条线段。然后概率就是用于切割这条线条的,我们要做的其实只需要随机产生一个数,然后看这个数对应于哪一条分线条就OK了。这样,数组不需太大,而且还能得到同样的效果。

    举个例子,A字母为30%概率,B字母为70%概率。那么可以把这条0——100的线段分割为2段。
    一段为0——30,为A字母;其余的分属B字段
    好了,程序现在随机抽取了30这个数,那么很明显,结果就是A了。

    |-----------|-----------------------|
    0           30                     100

    可是接下来写的实现代码,现在看来可真是乱七八糟的。后来youd提出了一个更好的算法并写出了php代码,让自己汗颜不已:

    youd:开始是从1,1000这个概率范围内筛选第一个数是否在他的出现概率范围之内,如果不在,则将概率空间,也就是k的值减去刚刚的那个数字的概率空间,在本例当中就是减去100,也就是说第二个数是在1,900这个范围内筛选的。我想应该很容易理解,这样筛选到最终,总会有一个数满足要求(比如说前三个都不幸成为了非Luck Num,那么k已经-100-200-300=400了,那么最后一个数无论如何也会满足要求的。相当于拿东西,第一个不是,第二个不是,第三个还不是,那最后一个一定是。

    这个算法的优点是,对于没有概率重叠的数字进行筛选,最多只需要遍览一次数组就足够了。程序简单,效率高

    一年后看回此贴,真是感慨自己当初写PHP代码的幼稚(当然,现在其实水平也很菜)。趁着现在写论文的休息时间,把youd的算法用函数给包装起来,以待以后使用。

    不知道当年一起讨论的人,现在如何了......

    代码如下:

    <?php
    /**
    * 根据概率取随机数的算法(仅抽取1个)
    * 用法:
    * $proArr = array(10,20,30,40);
    * $result = pro_rand($proArr);
    * echo '你抽到的$proArr数组索引是'. $result. ',其预设概率数是'. $proArr[$result];
    *
    * @author Horse Luke, 2010. Special Thanks to houlai, youd
    * @link
    http://www.phpchina.com/bbs/viewthread.php?tid=104235
    * @param array $proArr 概率数组。格式为array('A'=>10, 'B'=>40, 'C'=>50),或者array(10,40,50)。数组的每个键值(value)必须为大于1的整数;所有数组键值(value)加起来即为其总概率精度
    * @return mixed $result 结果,将返回抽取到的概率数组索引值。
    */
    function pro_rand( $proArr ){
        $result = '';

        //概率数组的总概率精度
        $proSum = array_sum($proArr);
       
        /*概率数组循环。算法为youd提供,以下为youd的口述流程(假设为array(100,200,300,400)):
        开始是从1,1000这个概率范围内筛选第一个数是否在他的出现概率范围之内,
        如果不在,则将概率空间,也就是k的值减去刚刚的那个数字的概率空间,在本例当中就是减去100,也就是说第二个数是在1,900这个范围内筛选的。
        我想应该很容易理解,这样筛选到最终,总会有一个数满足要求
        (比如说前三个都不幸成为了非Luck Num,那么k已经-100-200-300=400了,那么最后一个数无论如何也会满足要求的)。
        相当于拿东西,第一个不是,第二个不是,第三个还不是,那最后一个一定是。
        这个算法的优点是,对于没有概率重叠的数字进行筛选,最多只需要遍览一次数组就足够了。程序简单,效率高
        */
        foreach ( $proArr as $key => $proCur ){
            $randNum = mt_rand(1, $proSum);
            if( $randNum <= $proCur ){
                $result = $key;
                break;
            }else{
                $proSum -= $proCur;
            }
        }
       
        return $result;
       
    }


    /**
    * 根据概率取随机数的算法(抽取多个,并且不重复)。
    * 依赖于函数pro_rand
    * 用法:
    * $proArr = array(10,20,30,40);
    * $result = pro_rand_unique_multi($proArr, 2);
    * var_export($result);
    *
    * @author Horse Luke, 2010. Special Thanks to houlai, youd
    * @link
    http://www.phpchina.com/bbs/viewthread.php?tid=104235
    * @param array $proArr 概率数组。格式为array('A'=>10, 'B'=>40, 'C'=>50),或者array(10,40,50)。数组的每个键值(value)必须为大于1的整数;所有数组键值(value)加起来即为其总概率精度
    * @param integer $num 指定抽取数目。数值不能大于概率数组的个数
    * @return array $result 结果,将返回指定抽取数目的概率数组索引值
    */
    function pro_rand_unique_multi( $proArr, $num = 1 ){
        $result = array();
        if( $num > count($proArr) ){
            trigger_error('The stack number of Probability Array is GREATER THAN you set!', 256);
        }
       
        while(1){
            if($num < 1){
                break;
            }
            $curResult = pro_rand($proArr);
            $result[] = $curResult;
            //重置总概率精度,有待概率论验证
            unset($proArr[$curResult]);
            $num -= 1;
        }
       
        return $result;
       
    }

     

    以上函数的测试代码:

    pro_rand:

    $proArr = array(10,20,30,40);
    mt_srand(time());

    $distribute = array();
    for($k = 1; $k <= 1000; $k++ ){

        $result = pro_rand($proArr);
        if(!isset($distribute[$result])){
            $distribute[$result] = 1;
        }else{
            $distribute[$result] += 1;
        }

    }

    ksort($distribute);
    var_export($distribute);

    pro_rand_unique_multi:

    $proArr = array(10,20,30,40);
    mt_srand(time());

    $distribute = array();
    for($k = 1; $k <= 1000; $k++ ){

        $result = pro_rand_unique_multi($proArr, 2);
        foreach($result as $key => $proKey){
            if(!isset($distribute[$proKey])){
                $distribute[$proKey] = 1;
            }else{
                $distribute[$proKey] += 1;
            }
        }

    }

    ksort($distribute);
    var_export($distribute);

    (完)

  • 相关阅读:
    09_ssh服务器的安装和使用
    08_linux下的文件压缩和解压
    38-自定义异常类
    37-手动抛出异常对象
    DataGrip 2020.1 安装与激活
    36-异常的处理
    35-异常
    node+ajax实战案例(1)
    ajax前后端交互原理(7)
    ajax前后端交互原理(5)
  • 原文地址:https://www.cnblogs.com/catkins/p/5270747.html
  • Copyright © 2011-2022 走看看