>  기사  >  웹 프론트엔드  >  js 배열의 가중치 확률 할당 방법 소개

js 배열의 가중치 확률 할당 방법 소개

巴扎黑
巴扎黑원래의
2017-09-13 09:19:561515검색

오늘은 페이지 캐러셀을 제어하는 ​​js 함수를 작성해봤습니다. 큐만 사용하면 매우 간단하지만 각 페이지에 가중치를 할당하는 것이 엄청나게 복잡해지는 것을 고려하면 스위치와 else로는 해결할 수 없기 때문에 사용해 보기로 했습니다. js 배열 구현

오늘은 페이지 캐러셀을 제어하는 ​​js 함수를 작성했습니다. 대기열만 사용하면 매우 간단하지만 각 페이지에 가중치를 할당하는 것이 극도로 복잡해지는지 고려하여 스위치를 사용하면 해결되지 않습니다. 그래서 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 = &#39;undefined&#39; != 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:&#39;1&#39;,weight:1.5},{name:&#39;2&#39;,weight:2.5},{name:&#39;3&#39;,weight:3.5}];
	//var arr=[{name:&#39;1&#39;,weight:&#39;15%&#39;},{name:&#39;2&#39;,weight:&#39;25%&#39;},{name:&#39;3&#39;,weight:&#39;35%&#39;}];
	//求出最大公约数以计算缩小倍数,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(&#39;undefined&#39; != typeof(arr[i].weight))
		{
			if(arr[i].weight.toString().indexOf(&#39;%&#39;) !== -1) {
				per = Math.floor(arr[i].weight.toString().replace(&#39;%&#39;,&#39;&#39;));
				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:&#39;1&#39;,weight:1.5},{name:&#39;2&#39;,weight:2.5},{name:&#39;3&#39;,weight:3.5}];
console.log(weight_rand(arr));
var arr=[{name:&#39;1&#39;,weight:&#39;15%&#39;},{name:&#39;2&#39;,weight:&#39;25%&#39;},{name:&#39;3&#39;,weight:&#39;35%&#39;}];
console.log(weight_rand(arr));
var prize_arr = [
	{&#39;id&#39;:1, &#39;prize&#39;:&#39;平板电脑&#39;, &#39;weight&#39;:1},
	{&#39;id&#39;:2, &#39;prize&#39;:&#39;数码相机&#39;, &#39;weight&#39;:2},
	{&#39;id&#39;:3, &#39;prize&#39;:&#39;音箱设备&#39;, &#39;weight&#39;:10},
	{&#39;id&#39;:4, &#39;prize&#39;:&#39;4G优盘&#39;, &#39;weight&#39;:12},
	{&#39;id&#39;:5, &#39;prize&#39;:&#39;10Q币&#39;, &#39;weight&#39;:22},
	{&#39;id&#39;:6, &#39;prize&#39;:&#39;下次没准就能中哦&#39;, &#39;weight&#39;: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 == &#39;平板电脑&#39;)
	{
		pingban++;
	}else if(prize.prize == &#39;数码相机&#39;){
		shuma++;
	}else if(prize.prize == &#39;音箱设备&#39;){
		yinxiang++;
	}else if(prize.prize == &#39;4G优盘&#39;){
		youpan++;
	}else if(prize.prize == &#39;10Q币&#39;){
		qb++;
	}else if(prize.prize == &#39;下次没准就能中哦&#39;){
		xc++;
	}
}

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

코드는 최대 공통 분모를 통해 첨자 배열에 최적화되었습니다. 숫자 비율 모드는 최소 숫자 비율로 최적화되었습니다. 백분율 모드는 소수점 2자리를 지원하지 않습니다. 성능 소모로 인해.

js 버전을 작성한 후 100,000번의 루프 테스트를 거친 후 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(&#39;name&#39;=>&#39;1&#39;,&#39;weight&#39;=>1.5),array(&#39;name&#39;=>&#39;2&#39;,&#39;weight&#39;=>1.5),array(&#39;name&#39;=>&#39;3&#39;,&#39;weight&#39;=>1.5));
  //$arr=array(array(&#39;name&#39;=>&#39;1&#39;,&#39;weight&#39;=>&#39;15%&#39;),array(&#39;name&#39;=>&#39;2&#39;,&#39;weight&#39;=>&#39;25%&#39;),array(&#39;name&#39;=>&#39;3&#39;,&#39;weight&#39;=>&#39;35%&#39;));
  //求出最大公约数以计算缩小倍数,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][&#39;weight&#39;]))
    {
      if(strpos($arr[$i][&#39;weight&#39;], &#39;%&#39;) !== false)
      {
        $per = floor(str_replace(&#39;%&#39;, &#39;&#39;, $arr[$i][&#39;weight&#39;]));
        $perMode = true;
      }else{
        $per = floor($arr[$i][&#39;weight&#39;]*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(&#39;name&#39;=>&#39;1&#39;,&#39;weight&#39;=>1.5),array(&#39;name&#39;=>&#39;2&#39;,&#39;weight&#39;=>1.5),array(&#39;name&#39;=>&#39;3&#39;,&#39;weight&#39;=>1.5));
p(weight_rand($arr));
$arr=array(array(&#39;name&#39;=>&#39;1&#39;,&#39;weight&#39;=>&#39;15%&#39;),array(&#39;name&#39;=>&#39;2&#39;,&#39;weight&#39;=>&#39;25%&#39;),array(&#39;name&#39;=>&#39;3&#39;,&#39;weight&#39;=>&#39;35%&#39;));
p(weight_rand($arr));

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

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


p($result);
p(&#39;耗费时间:&#39;.$cost.&#39;秒&#39;);
function p($var)
{
  echo "<pre class="brush:php;toolbar:false">";
  if($var === false)
  {
    echo &#39;false&#39;;
  }else if($var === &#39;&#39;){
    print_r("&#39;&#39;");
  }else{
    print_r($var);
  }
  echo "
"; }

PHP 버전이 정수비 모드만 사용한다면 숫자의 증폭이나 최소 공배수를 찾는 알고리즘을 고려할 필요가 없고 단순 누적만 하면 되므로 시간이 크게 단축될 수 있습니다. 실행 시간.

위 내용은 js 배열의 가중치 확률 할당 방법 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.