首页 web前端 js教程 js数组的权重概率分配方法介绍

js数组的权重概率分配方法介绍

Sep 13, 2017 am 09:19 AM
javascript 分配 概率

今天写了一个js控制页面轮播的功能,如果仅仅使用队列很简单,但是考虑到为每一个页面分配权重的是否变的异常复杂,使用switch和if else也无法解决,于是想到使用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 = &#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版,于是很轻松改为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(&#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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

用于时间序列概率预测的分位数回归 用于时间序列概率预测的分位数回归 May 07, 2024 pm 05:04 PM

不要改变原内容的意思,微调内容,重写内容,不要续写。“分位数回归满足这一需求,提供具有量化机会的预测区间。它是一种统计技术,用于模拟预测变量与响应变量之间的关系,特别是当响应变量的条件分布命令人感兴趣时。与传统的回归方法不同,分位数回归侧重于估计响应变量变量的条件量值,而不是条件均值。”图(A):分位数回归分位数回归概念分位数回归是估计⼀组回归变量X与被解释变量Y的分位数之间线性关系的建模⽅法。现有的回归模型实际上是研究被解释变量与解释变量之间关系的一种方法。他们关注解释变量与被解释变量之间的关

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

javascript中如何使用insertBefore javascript中如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

See all articles