扫码签到小程序_js数组完成权重概率分配

js数组实现权重概率分配       今天写了一个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 = 'undefined' != 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]];
}

上面的方法虽然可行,可是遇到这样一个问题:对于一般复杂的分配情况如分配(相对值)可以满足,如果遇到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:'1',weight:1.5},{name:'2',weight:2.5},{name:'3',weight:3.5}];
 //var arr=[{name:'1',weight:'15%'},{name:'2',weight:'25%'},{name:'3',weight:'35%'}];
 //求出最大公约数以计算缩小倍数,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 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('undefined' != typeof(arr[i].weight))
 if(arr[i].weight.toString().indexOf('%') !== -1) {
 per = Math.floor(arr[i].weight.toString().replace('%',''));
 perMode = true;
 }else{
 per = Math.floor(arr[i].weight*100);
 }else{
 per = 0;
 weight_arr[i] = per;
 maxNum = Math.gcd(maxNum, per);
 //数字比模式,,其组成[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:'1',weight:1.5},{name:'2',weight:2.5},{name:'3',weight:3.5}];
console.log(weight_rand(arr));
var arr=[{name:'1',weight:'15%'},{name:'2',weight:'25%'},{name:'3',weight:'35%'}];
console.log(weight_rand(arr));
var prize_arr = [
 {'id':1, 'prize':'平板电脑', 'weight':1},
 {'id':2, 'prize':'数码相机', 'weight':2},
 {'id':3, 'prize':'音箱设备', 'weight':10},
 {'id':4, 'prize':'4G优盘', 'weight':12},
 {'id':5, 'prize':'10Q币', 'weight':22},
 {'id':6, 'prize':'下次没准就能中哦', 'weight':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 == '平板电脑')
 pingban++;
 }else if(prize.prize == '数码相机'){
 shuma++;
 }else if(prize.prize == '音箱设备'){
 yinxiang++;
 }else if(prize.prize == '4G优盘'){
 youpan++;
 }else if(prize.prize == '10Q币'){
 qb++;
 }else if(prize.prize == '下次没准就能中哦'){
 xc++;
var stop = new Date().getTime();
console.log('平板电脑:'+pingban/times+', 数码相机:'+shuma/times+', 音箱设备:'+yinxiang/times+', 4G优盘:'+youpan/times+', 10Q币:'+qb/times+', 下次没准就能中哦:'+xc/times);
console.log('耗费时间:'+(stop-start)/1000+'秒');

该代码已经通过最大公约数对下标数组进行优化,使用数字比模式已经优化到最小数值比例,百分比模式考虑性能消耗暂不支持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('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 $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);
 //数字比模式,,其组成[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;

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


相关阅读