js數(shù)組實(shí)現(xiàn)權(quán)重概率分配
今天寫(xiě)了一個(gè)js控制頁(yè)面輪播的功能,如果僅僅使用隊(duì)列很簡(jiǎn)單,但是考慮到為每一個(gè)頁(yè)面分配權(quán)重的是否變的異常復(fù)雜,使用switch和if else也無(wú)法解決,于是想到使用js數(shù)組實(shí)現(xiàn),思路是將各個(gè)輪播的頁(yè)面抽象成一個(gè)對(duì)象,各個(gè)對(duì)象需要手動(dòng)指定權(quán)重值,然后組成一個(gè)數(shù)組,使用下面封裝的函數(shù),將會(huì)根據(jù)各個(gè)對(duì)象相應(yīng)的權(quán)重概率返回一個(gè)對(duì)象,代碼如下:
/**
* js數(shù)組實(shí)現(xiàn)權(quán)重概率分配
* @param Array arr js數(shù)組,參數(shù)類(lèi)型[Object,Object,Object……]
* @return Array 返回一個(gè)隨機(jī)元素,概率為其percent/所有percent之和,參數(shù)類(lèi)型Object
* @author shuiguang
*/
function weight_rand(arr){
//參數(shù)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;
//下標(biāo)標(biāo)記數(shù)組,按照上面的例子,單倍情況下其組成為[1,2,2,3,4,4]
var index = new Array();
for (i = 0; i < arr.length; i++) {
//判斷元素的權(quán)重,為了實(shí)現(xiàn)小數(shù)權(quán)重,先將所有的值放大100倍
percent = 'undefined' != typeof(arr[i].percent) ? parseInt(arr[i].percent*100) : 0;
for (j = 0; j < percent; j++) {
index.push(i);
}
total += percent;
}
//隨機(jī)數(shù)值,其值介于0-5的整數(shù)
var rand = Math.floor(Math.random() * total);
return arr[index[rand]];
}
上面的方法雖然可行,可是遇到這樣一個(gè)問(wèn)題:對(duì)于一般復(fù)雜的分配情況如1:1:1分配(相對(duì)值)可以滿足,如果遇到15%,25%,35%剩余等精確權(quán)重分配(絕對(duì)值)無(wú)法滿足。因?yàn)槿ビ?jì)算15%:25%:35%:剩余的比例很是麻煩,于是我將上面的函數(shù)繼續(xù)修改,添加了百分比模式,比如上面的例子,分配了上面明確的百分?jǐn)?shù)之后,剩余的百分比將給最后一個(gè)元素,而不用計(jì)算最后一個(gè)元素占的百分?jǐn)?shù),也不用計(jì)算各個(gè)元素的比例。代碼如下:
/**
* js數(shù)組實(shí)現(xiàn)權(quán)重概率分配,支持?jǐn)?shù)字比模式(支持2位小數(shù))和百分比模式(不支持小數(shù),最后一個(gè)元素多退少補(bǔ))
* @param Array arr js數(shù)組,參數(shù)類(lèi)型[Object,Object,Object……]
* @return Array 返回一個(gè)隨機(jī)元素,概率為其weight/所有weight之和,參數(shù)類(lèi)型Object
* @author shuiguang
*/
function weight_rand(arr){
//參數(shù)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%'}];
//求出最大公約數(shù)以計(jì)算縮小倍數(shù),perMode為百分比模式
var per;
var maxNum = 0;
var perMode = false;
//自定義Math求最小公約數(shù)方法
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元素對(duì)象拷貝仍然會(huì)造成浪費(fèi),但是使用權(quán)重?cái)?shù)組對(duì)應(yīng)關(guān)系更省內(nèi)存
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);
}
//數(shù)字比模式,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表示存儲(chǔ)arr下標(biāo)的數(shù)據(jù)塊長(zhǎng)度,已優(yōu)化至最小整數(shù)形式減小索引數(shù)組的長(zhǎng)度
len = weight_arr[i];
for (j = 0; j < len; j++){
//超過(guò)100%跳出,后面的舍棄
if(total >= 100){
break;
}
index.push(i);
total++;
}
}
//使用最后一個(gè)元素補(bǔ)齊100%
while(total < 100){
index.push(arr.length-1);
total++;
}
}else{
for (i = 0; i < arr.length; i++) {
//len表示存儲(chǔ)arr下標(biāo)的數(shù)據(jù)塊長(zhǎng)度,已優(yōu)化至最小整數(shù)形式減小索引數(shù)組的長(zhǎng)度
len = weight_arr[i]/maxNum;
for (j = 0; j < len; j++){
index.push(i);
}
total += len;
}
}
//隨機(jī)數(shù)值,其值為0-11的整數(shù),數(shù)據(jù)塊根據(jù)權(quán)重分塊
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':'數(shù)碼相機(jī)', 'weight':2},
{'id':3, 'prize':'音箱設(shè)備', 'weight':10},
{'id':4, 'prize':'4G優(yōu)盤(pán)', 'weight':12},
{'id':5, 'prize':'10Q幣', 'weight':22},
{'id':6, 'prize':'下次沒(méi)準(zhǔn)就能中哦', '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 == '數(shù)碼相機(jī)'){
shuma++;
}else if(prize.prize == '音箱設(shè)備'){
yinxiang++;
}else if(prize.prize == '4G優(yōu)盤(pán)'){
youpan++;
}else if(prize.prize == '10Q幣'){
qb++;
}else if(prize.prize == '下次沒(méi)準(zhǔn)就能中哦'){
xc++;
}
}
var stop = new Date().getTime();
console.log('平板電腦:'+pingban/times+', 數(shù)碼相機(jī):'+shuma/times+', 音箱設(shè)備:'+yinxiang/times+', 4G優(yōu)盤(pán):'+youpan/times+', 10Q幣:'+qb/times+', 下次沒(méi)準(zhǔn)就能中哦:'+xc/times);
console.log('耗費(fèi)時(shí)間:'+(stop-start)/1000+'秒');
該代碼已經(jīng)通過(guò)最大公約數(shù)對(duì)下標(biāo)數(shù)組進(jìn)行優(yōu)化,使用數(shù)字比模式已經(jīng)優(yōu)化到最小數(shù)值比例,百分比模式考慮性能消耗暫不支持2位小數(shù)。
寫(xiě)完js版,于是很輕松改為php版本,經(jīng)過(guò)10萬(wàn)次循環(huán)測(cè)試,發(fā)現(xiàn)for循環(huán)比f(wàn)oreach省時(shí)間,而非網(wǎng)上傳的foreach比f(wàn)or更快。但是總體來(lái)說(shuō),js的執(zhí)行速度是php的20倍左右,php的執(zhí)行時(shí)間約6秒,js的執(zhí)行時(shí)間約為0.346秒。
/**
* php數(shù)組實(shí)現(xiàn)權(quán)重概率分配,支持?jǐn)?shù)字比模式(支持2位小數(shù))和百分比模式(不支持小數(shù),最后一個(gè)元素多退少補(bǔ))
* @param array $arr php數(shù)組,參數(shù)類(lèi)型array(array(),array(),array()……)
* @return array 返回一個(gè)隨機(jī)元素,概率為其percent/所有percent之和,參數(shù)類(lèi)型array()
* @author shuiguang
*/
function weight_rand($arr)
{
//參數(shù)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%'));
//求出最大公約數(shù)以計(jì)算縮小倍數(shù),perMode為百分比模式
$perMode = false;
$maxNum = 0;
//自定義求最小公約數(shù)方法
$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;
};
//使用傳地址可能會(huì)影響后面的結(jié)果,但是使用權(quán)重?cái)?shù)組對(duì)應(yīng)關(guān)系更省內(nèi)存
$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);
}
//數(shù)字比模式,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表示存儲(chǔ)$arr下標(biāo)的數(shù)據(jù)塊長(zhǎng)度,已優(yōu)化至最小整數(shù)形式減小索引數(shù)組的長(zhǎng)度
$len = $weight_arr[$i];
for ($j = 0; $j < $len; $j++)
{
//超過(guò)100%跳出,后面的舍棄
if($total >= 100)
{
break;
}
$index[] = $i;
$total++;
}
}
//使用最后一個(gè)元素補(bǔ)齊100%
while($total < 100)
{
$index[] = $arr_len-1;
$total++;
}
}else{
for($i=0; $i<$arr_len; $i++)
{
//len表示存儲(chǔ)arr下標(biāo)的數(shù)據(jù)塊長(zhǎng)度,已優(yōu)化至最小整數(shù)形式減小索引數(shù)組的長(zhǎng)度
$len = $weight_arr[$i]/$maxNum;
for ($j = 0; $j < $len; $j++)
{
$index[] = $i;
}
$total += $len;
}
}
//隨機(jī)數(shù)值,其值為0-11的整數(shù),數(shù)據(jù)塊根據(jù)權(quán)重分塊
$rand = floor(mt_rand(0, $total));
//修復(fù)php隨機(jī)函數(shù)可以取臨界值造成的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'=>'數(shù)碼相機(jī)', 'weight'=>5),
'2' => array('id'=>3, 'prize'=>'音箱設(shè)備', 'weight'=>10),
'3' => array('id'=>4, 'prize'=>'4G優(yōu)盤(pán)', 'weight'=>12),
'4' => array('id'=>5, 'prize'=>'10Q幣', 'weight'=>22),
'5' => array('id'=>6, 'prize'=>'下次沒(méi)準(zhǔn)就能中哦', '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('耗費(fèi)時(shí)間:'.$cost.'秒');
function p($var)
{
echo "<pre>";
if($var === false)
{
echo 'false';
}else if($var === ''){
print_r("''");
}else{
print_r($var);
}
echo "</pre>";
}
php版本如果只是使用整數(shù)數(shù)字比模式,完全不用考慮數(shù)字的放大與求最小公倍數(shù)的算法,只需要做簡(jiǎn)單的累加即可,可以大大縮短執(zhí)行時(shí)間。
- jquery.rotate.js實(shí)現(xiàn)可選抽獎(jiǎng)次數(shù)和中獎(jiǎng)內(nèi)容的轉(zhuǎn)盤(pán)抽獎(jiǎng)代碼
- js控制隨機(jī)數(shù)生成概率代碼實(shí)例
- js實(shí)現(xiàn)1,2,3,5數(shù)字按照概率生成
- js 概率計(jì)算(簡(jiǎn)單版)
- 詳解JavaScript按概率隨機(jī)生成事件
- javascript實(shí)現(xiàn)隨機(jī)抽獎(jiǎng)功能
- js實(shí)現(xiàn)抽獎(jiǎng)功能
- 原生JS實(shí)現(xiàn)九宮格抽獎(jiǎng)
- js實(shí)現(xiàn)簡(jiǎn)單抽獎(jiǎng)功能
- JS根據(jù)獎(jiǎng)品權(quán)重計(jì)算中獎(jiǎng)概率
相關(guān)文章
一個(gè)Action如何調(diào)用兩個(gè)不同的方法
這篇文章主要介紹了一個(gè)Action如何調(diào)用兩個(gè)不同的方法,需要的朋友可以參考下2014-05-05
JavaScript中的this陷阱的最全收集并整理(沒(méi)有之一)
這篇文章主要介紹了JavaScript中的this陷阱的最全收集,需要的朋友可以參考下2017-02-02
javascript 字符串連接的性能問(wèn)題(多瀏覽器)
今天看了javascript 高級(jí)程序設(shè)計(jì) 談到了字符串連接的性能問(wèn)題2008-11-11
uniapp開(kāi)發(fā)微信小程序遇到的問(wèn)題筆記
這篇文章主要給大家介紹了關(guān)于uniapp開(kāi)發(fā)微信小程序遇到的問(wèn)題的相關(guān)資料,比較適合第一次使用uniapp?開(kāi)發(fā)微信小程序的伙伴,或者沒(méi)有過(guò)實(shí)戰(zhàn)經(jīng)驗(yàn)的小伙伴參考,需要的朋友可以參考下2023-01-01
js 禁用只讀文本框獲得焦點(diǎn)時(shí)的退格鍵
發(fā)現(xiàn)只讀文本框有一個(gè)缺陷,當(dāng)鼠標(biāo)焦點(diǎn)在文本框里面的時(shí)候按回退鍵(backSpace), 會(huì)退回到前一個(gè)頁(yè)面2010-04-04

