js数组实现权重概率分配
今天写了一个js控制页面轮播的功能,如果仅仅使用队列很简单,但是考虑到为每一个页面分配权重的是否变的异常复杂,使用switch和ifelse也无法解决,于是想到使用js数组实现,思路是将各个轮播的页面抽象成一个对象,各个对象需要手动指定权重值,然后组成一个数组,使用下面封装的函数,将会根据各个对象相应的权重概率返回一个对象,代码如下:
/**
*js数组实现权重概率分配
*@paramArrayarrjs数组,参数类型[Object,Object,Object……]
*@returnArray返回一个随机元素,概率为其percent/所有percent之和,参数类型Object
*@authorshuiguang
*/
functionweight_rand(arr){
//参数arr元素必须含有percent属性,参考如下所示
/*
vararr=[{
name:'1',
percent:1
},{
name:'2',
percent:2
},{
name:'3',
percent:1
},{
name:'4',
percent:2
}
];
*/
vartotal=0;
vari,j,percent;
//下标标记数组,按照上面的例子,单倍情况下其组成为[1,2,2,3,4,4]
varindex=newArray();
for(i=0;i
上面的方法虽然可行,可是遇到这样一个问题:对于一般复杂的分配情况如1:1:1分配(相对值)可以满足,如果遇到15%,25%,35%剩余等精确权重分配(绝对值)无法满足。因为去计算15%:25%:35%:剩余的比例很是麻烦,于是我将上面的函数继续修改,添加了百分比模式,比如上面的例子,分配了上面明确的百分数之后,剩余的百分比将给最后一个元素,而不用计算最后一个元素占的百分数,也不用计算各个元素的比例。代码如下:
/**
*js数组实现权重概率分配,支持数字比模式(支持2位小数)和百分比模式(不支持小数,最后一个元素多退少补)
*@paramArrayarrjs数组,参数类型[Object,Object,Object……]
*@returnArray返回一个随机元素,概率为其weight/所有weight之和,参数类型Object
*@authorshuiguang
*/
functionweight_rand(arr){
//参数arr元素必须含有weight属性,参考如下所示
//vararr=[{name:'1',weight:1.5},{name:'2',weight:2.5},{name:'3',weight:3.5}];
//vararr=[{name:'1',weight:'15%'},{name:'2',weight:'25%'},{name:'3',weight:'35%'}];
//求出最大公约数以计算缩小倍数,perMode为百分比模式
varper;
varmaxNum=0;
varperMode=false;
//自定义Math求最小公约数方法
Math.gcd=function(a,b){
varmin=Math.min(a,b);
varmax=Math.max(a,b);
varresult=1;
if(a===0||b===0){
returnmax;
}
for(vari=min;i>=1;i--){
if(min%i===0&&max%i===0){
result=i;
break;
}
}
returnresult;
};
//使用clone元素对象拷贝仍然会造成浪费,但是使用权重数组对应关系更省内存
varweight_arr=newArray();
for(i=0;i=100){
break;
}
index.push(i);
total++;
}
}
//使用最后一个元素补齐100%
while(total<100){
index.push(arr.length-1);
total++;
}
}else{
for(i=0;i
该代码已经通过最大公约数对下标数组进行优化,使用数字比模式已经优化到最小数值比例,百分比模式考虑性能消耗暂不支持2位小数。
写完js版,于是很轻松改为php版本,经过10万次循环测试,发现for循环比foreach省时间,而非网上传的foreach比for更快。但是总体来说,js的执行速度是php的20倍左右,php的执行时间约6秒,js的执行时间约为0.346秒。
/**
*php数组实现权重概率分配,支持数字比模式(支持2位小数)和百分比模式(不支持小数,最后一个元素多退少补)
*@paramarray$arrphp数组,参数类型array(array(),array(),array()……)
*@returnarray返回一个随机元素,概率为其percent/所有percent之和,参数类型array()
*@authorshuiguang
*/
functionweight_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.'秒');
functionp($var)
{
echo"";
if($var===false)
{
echo'false';
}elseif($var===''){
print_r("''");
}else{
print_r($var);
}
echo"";
}
php版本如果只是使用整数数字比模式,完全不用考虑数字的放大与求最小公倍数的算法,只需要做简单的累加即可,可以大大缩短执行时间。