今天寫了一個(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ù)類型[Object,Object,Object……] * @return Array 返回一個(gè)隨機(jī)元素,概率為其percent/所有percent之和,參數(shù)類型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ù)類型[Object,Object,Object……] * @return Array 返回一個(gè)隨機(jī)元素,概率為其weight/所有weight之和,參數(shù)類型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)盤', '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)盤'){ 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)盤:'+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ù)。
寫完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ù)類型array(array(),array(),array()……) * @return array 返回一個(gè)隨機(jī)元素,概率為其percent/所有percent之和,參數(shù)類型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)盤', '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í)間。
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com