【js数组转字符串】js数组实现权重概率分配

时间:2021-08-22  来源:js教程  阅读:

今天写了一个js控制页面轮播的功能,如果仅仅使用队列很简单,但是考虑到为每一个页面分配权重的是否变的异常复杂,使用switch和if else也无法解决,于是想到使用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",
      percent : 1
    }, {
      name : "4",
      percent : 2
    }
  ];
  */
  var total = 0;
  var i, j, percent;
  //下标标记数组,按照上面的例子,单倍情况下其组成为[1,2,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]];
}

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

/**
* js数组实现权重概率分配,支持数字比模式(支持2位小数)和百分比模式(不支持小数,最后一个元素多退少补)
* @param  Array  arr  js数组,参数类型[Object,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%"},{name:"2",weight:"25%"},{name:"3",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].weight*100);
			}
		}else{
			per = 0;
		}
		weight_arr[i] = per;
		maxNum = Math.gcd(maxNum, per);
	}
	//数字比模式,3:5:7,其组成[0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 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:1.5},{name:"2",weight:2.5},{name:"3",weight:3.5}];
console.log(weight_rand(arr));
var arr=[{name:"1",weight:"15%"},{name:"2",weight:"25%"},{name:"3",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平板电脑")
	{
		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+"秒");

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

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

/**
* php数组实现权重概率分配,支持数字比模式(支持2位小数)和百分比模式(不支持小数,最后一个元素多退少补)
* @param  array  $arr  php数组,参数类型array(array(),array(),array()……)
* @return  array      返回一个随机元素,概率为其percent/所有percent之和,参数类型array()
* @author  shuiguang
*/
function weight_rand($arr)
{
  //参数arr元素必须含有percent属性,参考如下所示
  //$arr=array(array("name"=>"1","weight"=>1.5),array("name"=>"2","weight"=>1.5),array("name"=>"3","weight"=>1.5));
  //$arr=array(array("name"=>"1","weight"=>"15%"),array("name"=>"2","weight"=>"25%"),array("name"=>"3","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, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 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),array("name"=>"2","weight"=>1.5),array("name"=>"3","weight"=>1.5));
p(weight_rand($arr));
$arr=array(array("name"=>"1","weight"=>"15%"),array("name"=>"2","weight"=>"25%"),array("name"=>"3","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 "
"; }

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

【js数组转字符串】js数组实现权重概率分配

http://m.bbyears.com/wangyezhizuo/137463.html

推荐访问:js删除数组元素
相关阅读 猜你喜欢
本类排行 本类最新