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