js数组实现权重概率分配

前端之家收集整理的这篇文章主要介绍了js数组实现权重概率分配前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

@H_404_0@今天写了一个js控制页面轮播的功能,如果仅仅使用队列很简单,但是考虑到为每一个页面分配权重的是否变的异常复杂,使用switch和if else也无法解决,于是想到使用js数组实现,思路是将各个轮播的页面抽象成一个对象,各个对象需要手动指定权重值,然后组成一个数组,使用下面封装的函数,将会根据各个对象相应的权重概率返回一个对象,代码如下:


<div class="jb51code">
<pre class="brush:js;">
/**

  • js数组实现权重概率分配

  • @param Array arr js数组,参数类型[Object,Object,Object……]

  • @return Array 返回一个随机元素,概率为其percent/所有percent之和,参数类型Object

  • @author shuiguang
    /
    function weight_rand(arr){
    //参数arr元素必须含有percent属性,参考如下所示
    /

    var arr = [{
    name : '1',percent : 1
    },{
    name : '2',percent : 2
    },{
    name : '3',{
    name : '4',percent : 2
    }
    ];
    /
    var total = 0;
    var i,j,percent;
    //下标标记数组,按照上面的例子,单倍情况下其组成为[1,2,3,4,4]
    var index = new Array();
    for (i = 0; i < arr.length; i++) {
    //判断元素的权重,为了实现小数权重,先将所有的值放大100倍
    percent = 'undefined' != typeof(arr[i].percent) ? parseInt(arr[i].percent
    100) : 0;
    for (j = 0; j < percent; j++) {
    index.push(i);
    }
    total += percent;
    }
    //随机数值,其值介于0-5的整数
    var rand = Math.floor(Math.random() * total);
    return arr[index[rand]];
    }

@H_404_0@上面的方法虽然可行,可是遇到这样一个问题:对于一般复杂的分配情况如1:1:1分配(相对值)可以满足,如果遇到15%,25%,35%剩余等精确权重分配(绝对值)无法满足。因为去计算15%:25%:35%:剩余的比例很是麻烦,于是我将上面的函数继续修改添加了百分比模式,比如上面的例子,分配了上面明确的百分数之后,剩余的百分比将给最后一个元素,而不用计算最后一个元素占的百分数,也不用计算各个元素的比例。代码如下:

  • js数组实现权重概率分配,支持数字比模式(支持2位小数)和百分比模式(不支持小数,最后一个元素多退少补)

  • @param Array arr js数组,参数类型[Object,Object……]

  • @return Array 返回一个随机元素,概率为其weight/所有weight之和,参数类型Object

  • @author shuiguang
    */
    function weight_rand(arr){
    //参数arr元素必须含有weight属性,参考如下所示
    //var arr=[{name:'1',weight:1.5},{name:'2',weight:2.5},{name:'3',weight:3.5}];
    //var arr=[{name:'1',weight:'15%'},weight:'25%'},weight:'35%'}];
    //求出最大公约数以计算缩小倍数,perMode为百分比模式
    var per;
    var maxNum = 0;
    var perMode = false;
    //自定义Math求最小公约数方法
    Math.gcd = function(a,b){
    var min = Math.min(a,b);
    var max = Math.max(a,b);
    var result = 1;
    if(a === 0 || b===0){
    return max;
    }
    for(var i=min; i>=1; i--){
    if(min % i === 0 && max % i === 0){
    result = i;
    break;
    }
    }
    return result;
    };

    //使用clone元素对象拷贝仍然会造成浪费,但是使用权重数组对应关系更省内存
    var weight_arr = new Array();
    for (i = 0; i < arr.length; i++) {
    if('undefined' != typeof(arr[i].weight))
    {
    if(arr[i].weight.toString().indexOf('%') !== -1) {
    per = Math.floor(arr[i].weight.toString().replace('%',''));
    perMode = true;
    }else{
    per = Math.floor(arr[i].weight100);
    }
    }else{
    per = 0;
    }
    weight_arr[i] = per;
    maxNum = Math.gcd(maxNum,per);
    }
    //数字比模式,3:5:7,其组成[0,1,2]
    //百分比模式,元素所占百分比为15%,25%,35%
    var index = new Array();
    var total = 0;
    var len = 0;
    if(perMode){
    for (i = 0; i < arr.length; i++) {
    //len表示存储arr下标的数据块长度,已优化至最小整数形式减小索引数组的长度
    len = weight_arr[i];
    for (j = 0; j < len; j++){
    //超过100%跳出,后面的舍弃
    if(total >= 100){
    break;
    }
    index.push(i);
    total++;
    }
    }
    //使用最后一个元素补齐100%
    while(total < 100){
    index.push(arr.length-1);
    total++;
    }
    }else{
    for (i = 0; i < arr.length; i++) {
    //len表示存储arr下标的数据块长度,已优化至最小整数形式减小索引数组的长度
    len = weight_arr[i]/maxNum;
    for (j = 0; j < len; j++){
    index.push(i);
    }
    total += len;
    }
    }
    //随机数值,其值为0-11的整数,数据块根据权重分块
    var rand = Math.floor(Math.random()
    total);
    //console.log(index);
    return arr[index[rand]];
    }

  • var arr=[{name:'1',weight:3.5}];
    console.log(weight_rand(arr));
    var arr=[{name:'1',weight:'35%'}];
    console.log(weight_rand(arr));
    var prize_arr = [
    {'id':1,'prize':'平板电脑','weight':1},{'id':2,'prize':'数码相机','weight':2},{'id':3,'prize':'音箱设备','weight':10},{'id':4,'prize':'4G优盘','weight':12},{'id':5,'prize':'10Q币','weight':22},{'id':6,'prize':'下次没准就能中哦','weight':50}
    ];

    var times = 100000;
    var prize;
    var pingban = 0;
    var shuma = 0;
    var yinxiang = 0;
    var youpan = 0;
    var qb = 0;
    var xc = 0;
    var start = new Date().getTime();

    for($i=0; $i<times; $i++){
    prize = weight_rand(prize_arr);
    if(prize.prize == '平板电脑')
    {
    pingban++;
    }else if(prize.prize == '数码相机'){
    shuma++;
    }else if(prize.prize == '音箱设备'){
    yinxiang++;
    }else if(prize.prize == '4G优盘'){
    youpan++;
    }else if(prize.prize == '10Q币'){
    qb++;
    }else if(prize.prize == '下次没准就能中哦'){
    xc++;
    }
    }

    var stop = new Date().getTime();
    console.log('平板电脑:'+pingban/times+',数码相机:'+shuma/times+',音箱设备:'+yinxiang/times+',4G优盘:'+youpan/times+',10Q币:'+qb/times+',下次没准就能中哦:'+xc/times);
    console.log('耗费时间:'+(stop-start)/1000+'秒');

    @H_404_0@该代码已经通过最大公约数对下标数组进行优化,使用数字比模式已经优化到最小数值比例,百分比模式考虑性能消耗暂不支持2位小数。

    @H_404_0@写完js版,于是很轻松改为PHP版本,经过10万次循环测试,发现for循环比foreach省时间,而非网上传的foreach比for更快。但是总体来说,js的执行速度是PHP的20倍左右,PHP的执行时间约6秒,js的执行时间约为0.346秒。

    '1','weight'=>1.5),array('name'=>'2',array('name'=>'3','weight'=>1.5)); //$arr=array(array('name'=>'1','weight'=>'15%'),'weight'=>'25%'),'weight'=>'35%')); //求出最大公约数以计算缩小倍数,perMode为百分比模式 $perMode = false; $maxNum = 0; //自定义求最小公约数方法 $gcd = function($a,$b) { $min = min($a,$b); $max = max($a,$b); $result = 1; if($a === 0 || $b === 0) { return $max; } for($i=$min; $i>=1; $i--) { if($min % $i === 0 && $max % $i === 0) { $result = $i; break; } } return $result; }; //使用传地址可能会影响后面的结果,但是使用权重数组对应关系更省内存 $weight_arr = array(); $arr_len = count($arr); for($i=0; $i<$arr_len; $i++) { if(isset($arr[$i]['weight'])) { if(strpos($arr[$i]['weight'],'%') !== false) { $per = floor(str_replace('%','',$arr[$i]['weight'])); $perMode = true; }else{ $per = floor($arr[$i]['weight']*100); } }else{ $per = 0; } $weight_arr[$i] = $per; $maxNum = call_user_func($gcd,$maxNum,$per); } //数字比模式,3:5:7,其组成[0,2] //百分比模式,元素所占百分比为15%,25%,35% $index = array(); $total = 0; if($perMode) { for($i=0; $i<$arr_len; $i++) { //$len表示存储$arr下标的数据块长度,已优化至最小整数形式减小索引数组的长度 $len = $weight_arr[$i]; for ($j = 0; $j < $len; $j++) { //超过100%跳出,后面的舍弃 if($total >= 100) { break; } $index[] = $i; $total++; } } //使用最后一个元素补齐100% while($total < 100) { $index[] = $arr_len-1; $total++; } }else{ for($i=0; $i<$arr_len; $i++) { //len表示存储arr下标的数据块长度,已优化至最小整数形式减小索引数组的长度 $len = $weight_arr[$i]/$maxNum; for ($j = 0; $j < $len; $j++) { $index[] = $i; } $total += $len; } } //随机数值,其值为0-11的整数,数据块根据权重分块 $rand = floor(mt_rand(0,$total)); //修复php随机函数可以取临界值造成的bug $rand = $rand == $total ? $total-1 : $rand; return $arr[$index[$rand]]; }

    $arr=array(array('name'=>'1','weight'=>1.5));
    p(weight_rand($arr));
    $arr=array(array('name'=>'1','weight'=>'35%'));
    p(weight_rand($arr));

    $prize_arr = array(
    '0' => array('id'=>1,'prize'=>'平板电脑','weight'=>1),'1' => array('id'=>2,'prize'=>'数码相机','weight'=>5),'2' => array('id'=>3,'prize'=>'音箱设备','weight'=>10),'3' => array('id'=>4,'prize'=>'4G优盘','weight'=>12),'4' => array('id'=>5,'prize'=>'10Q币','weight'=>22),'5' => array('id'=>6,'prize'=>'下次没准就能中哦','weight'=>50),);

    $start = time();
    $result = array();
    $times = 100000;
    for($i=0; $i<$times; $i++)
    {
    $row = weight_rand($prize_arr);
    if(array_key_exists($row['prize'],$result))
    {
    $result[$row['prize']] ++;
    }else{
    $result[$row['prize']] = 1;
    }
    }
    $cost = time() - $start;

    p($result);
    p('耗费时间:'.$cost.'秒');
    function p($var)
    {
    echo "

    ";
    if($var === false)
    {
    echo 'false';
    }else if($var === ''){
    print_r("''");
    }else{
    print_r($var);
    }
    echo "
    ";
    }

    @H_404_0@PHP版本如果只是使用整数数字比模式,完全不用考虑数字的放大与求最小公倍数的算法,只需要做简单的累加即可,可以大大缩短执行时间。

    猜你在找的JavaScript相关文章