Maison  >  Article  >  interface Web  >  Comment implémenter le tri de probabilité de poids dans un tableau js

Comment implémenter le tri de probabilité de poids dans un tableau js

php中世界最好的语言
php中世界最好的语言original
2018-04-17 11:54:222212parcourir

Cette fois, je vais vous montrer comment implémenter le tri de probabilité de poids pour les tableaux js. Quelles sont les précautions pour implémenter le tri de probabilité de poids pour les tableaux js. Voici des cas pratiques, jetons un coup d'œil. .

Aujourd'hui, j'ai écrit une fonction js pour contrôler le carrousel des pages. C'est très simple si vous utilisez simplement la file d'attente, mais en considérant si l'attribution d'un poids à chaque page devient extrêmement compliquée, utilisez switch et if. Sinon, je n'ai pas pu le résoudre, j'ai donc pensé à utiliser un tableau js pour l'implémenter. L'idée est de résumer chaque page du carrousel dans un objet. Chaque objet doit spécifier manuellement la valeur de poids, puis former un tableau à l'aide de la fonction. encapsulé ci-dessous, il sera basé sur chaque objet. La probabilité de poids renvoie un objet, le code est le suivant :

/**
* 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]];
}

Bien que la méthode ci-dessus soit réalisable, elle rencontre le problème suivant : pour les situations d'allocation complexes générales telles que l'allocation 1:1:1 (valeur relative), elle peut être satisfaite si vous rencontrez une allocation de poids précise telle que 15 %, 25 %, Les 35% restants (valeur absolue) ne peuvent être satisfaits. Parce qu'il est très difficile de calculer le rapport restant de 15 % : 25 % : 35 %, j'ai continué à modifier la fonction ci-dessus et ajouté un mode de pourcentage. Par exemple, dans l'exemple ci-dessus, après avoir attribué le pourcentage clair ci-dessus, le reste. pourcentage sera Donner le dernier élément sans calculer le pourcentage du dernier élément ni la proportion de chaque élément. Le code est le suivant :

/**
* 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;);

Ce code a été optimisé pour le tableau d'indices via le plus grand dénominateur commun. Le mode rapport numérique a été optimisé pour le rapport numérique minimum. Le mode pourcentage ne prend actuellement pas en charge 2 décimales en raison de la consommation de performances.

Après avoir écrit la version js, je l'ai facilement changée en version php. Après 100 000 tests de boucle, j'ai trouvé que la for loop fait gagner du temps que foreach, et le foreach téléchargé en ligne est plus rapide que for. . Mais en général, la vitesse d'exécution de js est environ 20 fois supérieure à celle de php. Le temps d'exécution de php est d'environ 6 secondes et le temps d'exécution de js est d'environ 0,346 seconde.

/**
* 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;=>'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][&#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;=>'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[&#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 'false';
  }else if($var === ''){
    print_r("''");
  }else{
    print_r($var);
  }
  echo "
"; }

Si la version PHP utilise uniquement le mode rapport entier, il n'est pas nécessaire de considérer l'amplification des nombres et l'algorithme de recherche du plus petit commun multiple, il suffit de faire une simple accumulation, ce qui peut réduire considérablement le temps d'exécution.

Je pense que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web chinois de php !

Lecture recommandée :

Liaison de 3 événements de clic de souris

Compatibilité de la fonction Date() dans les navigateurs Gestion des problèmes


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn