作為一個成長中的架構師,編碼能力是萬不能停止的,這個算法是之前在上一家單位幫助同事們自助訂餐寫的,純愛好自己碼敲的,剛好這段時間重新整理代碼,發現了它,分享給大家,請大家品評指教。
- 使用場景介紹:隨着各種訂餐APP的出現,找飯館團購券,為自己訂點好吃的或者團購固定人數的券都很方便,但是很多時候我們遇到的是這樣的場景:知道用餐人數是幾人,但是總經費或人均消費標准有限制,讓你點菜,你就得一方面考慮葷素搭配,有菜有湯有主食,另一方面還得考慮經費限制;還有一種情況是就這么多總經費,人數又不確定(如之前答應去臨走又有事告假的),人少可以多點幾個硬菜,人多只能綜合考慮,拿主食頂上。這兩種場景,對點菜的人提出了很高的要求,本算法就是針對此種情況,只要給出用餐人數或固定金額,自動為你科學點菜,媽媽再也不用擔心你是個點菜盲。
- 基本效果截圖:
如上圖所示,輸入人數點擊開始點菜,系統自動會為你按照人均20的標准給出合理的菜單,這個人均標准是系統默認設置的,可以調整參數。如果輸入人數的同時輸入限定金額,則會以此金額為總花費的參考,保證不超過此金額下最優的給出建議菜單。當然如果同時輸入了人數和限定金額,那么限定金額/人數不能低於系統設置的人均最低值,比如人數6人,限定金額50,人均很不到10塊,下個毛管子。
3.基本原理:根據人數或限定金額得到本次菜單的可用總金額,同時根據人數按照一定葷素比例計算各類菜需要的個數,如6個人需要三個肉菜,一個蔬菜,一個涼菜,6分主食。(這個菜個數的計算是隨機的,涼菜隨機出現,主食可以是米飯也可以是餃子之類的,這個也是隨機的。同時蔬菜和肉菜的比例雖然固定,但是每次隨機會有小的調整,有上下浮動。);得到每類菜的個數后開始從對應類別中隨機選擇,得到結果后按照金額的限制先排序再進行適當剔除或重選,使得總限定金額最優化,最后得到菜單並輸出。
4.核心JS函數解釋說明:
-
- 初始化默認參數:
1 var dishRate=[0.4,0.5,0.1]; // meat,vege,cold 2 var leastAveragePayed = 10; // Average consumption money 3 var defaultAveragePayed = 20; // default consumption money 4 var eatRiceFlag = true; // if eat rice or other things 5 var eatRiceRate = 8; // the rate people eat rice; 6 var eachRicePayed = 3; // each rice cost how much 7 var moneyLimit = false; 8 var outRangeMoney = 5; // can over money
9 var allDishArray = []; // 飯店所有的菜品基本參數有:葷蔬搭配比例、人均最少消費、默認人均消費、要米飯還是其他主食、要米飯的概率、每碗米飯的價錢、是否有總消費限制、上下浮動的空間、飯店所有的菜品(這個需要初始化,將菜品按葷蔬涼菜湯米飯等類別分開,具體代碼沒有貼上來,看附件里)
- 點擊“開始點菜"執行的方式解釋:
1 function execute(){ 2 var peoples=eatPeoples.value; 3 var money=payMoney.value; 4 if("" == peoples){ 5 resultMes.innerText = "請輸入用餐人數!"; 6 return; 7 } 8 if(!/^\d+$/.test(peoples) || (("" != money) && !/^\d+$/.test(money))){ 9 resultMes.innerText = "輸入格式不對,請重新輸入!"; 10 return; 11 } 12 if(""!=money.replace(/[\s]+/g,"")){ 13 moneyLimit = true; 14 } 15 randomChooseDish(peoples,money); 16 }
做了一些基本的輸入有效性驗證,比如人數不能為空,輸入格式校驗等,然后進入randomChooseDish方法開始點菜
-
randomChooseDish方法如下:
1 function randomChooseDish(peoples,money){ 2 var tempPeoples=parseInt(peoples); 3 var tempSumMoney= (""==money)?tempPeoples*parseInt(defaultAveragePayed):parseInt(money); 4 if(!checkCondition(tempPeoples,tempSumMoney)){ 5 return; 6 } 7 var dishNumArray= getDishNumArray(tempPeoples); //get dishNumArray 8 9 var hasPayedMoney=0; 10 if(eatRiceFlag){ 11 // eat rice,reduce the rice money 12 hasPayedMoney = eachRicePayed*tempPeoples; 13 } 14 15 var beenChoosedArray = beginChooseDishesAndIndexs(dishNumArray); 16 17 sortChoosedArray(beenChoosedArray); 18 // when dishes are been choosed ,should check 19 checkAndChangeDishes(beenChoosedArray,hasPayedMoney,tempSumMoney); 20 21 // show result 22 showChooseResult(beenChoosedArray,hasPayedMoney,tempPeoples); 23 }
確定人數和總金額,checkCondition做基本的條件判斷,比如人數不能少於2人,總金額/人數不能低於人均最低值等;getDishNumArray用於根據人數和初始化葷素比例計算每類菜品需要點的數量;beginChooseDishesAndIndexs用於開始隨機點菜;sortChoosedArray用於排序,從貴到便宜,這樣對於便宜的菜可以有更多搭配的方式;checkAndChangeDishes用於對選擇的菜進行金額限制檢查,如果超過限制則開始從最便宜的菜調整菜,直到菜單合格;showChooseResult用於將結果顯示到頁面上。下面是具體每個函數的源碼,有注釋。
- checkCondition做基本的條件判斷
1 function checkCondition(tempPeoples,tempSumMoney){ 2 if(tempPeoples<2){ 3 //alert(); 4 resultMes.innerText = "一個人下館子?太奢侈了."; 5 return false; 6 } 7 if(tempPeoples>25){ 8 //alert(); 9 resultMes.innerText = "人數太多,一桌坐不下!"; 10 return false; 11 } 12 13 if(tempSumMoney<tempPeoples*leastAveragePayed){ 14 //alert(); 15 resultMes.innerText ="太摳了吧,都不到人均消費10塊!"; 16 return false; 17 } 18 return true; 19 }
- getDishNumArray用於根據人數和初始化葷素比例計算每類菜品需要點的數量
1 // get meat,vege,cold numArray 2 function getDishNumArray(tempPeoples){ 3 var numArray=[Math.ceil(tempPeoples*dishRate[0]),getRandomRate(8)?Math.ceil(tempPeoples*dishRate[1]):Math.floor(tempPeoples*dishRate[1]),Math.round(tempPeoples*dishRate[2])]; // meat,vege,cold 4 5 if(getSumArray(numArray)<=tempPeoples+1 || tempPeoples>=10){ 6 var soupNum = Math.floor(tempPeoples/4) 7 numArray[numArray.length]=soupNum>2?2:soupNum; // add soup,soup num small then 2 8 } 9 10 eatRiceFlag = getRandomRate(eatRiceRate); 11 if(!eatRiceFlag){ 12 // eat others 13 var mainRiceNum = Math.floor(tempPeoples/3); 14 numArray[numArray.length]=mainRiceNum>5?5:mainRiceNum; // add rice, mainrice nums small then 5 15 } 16 return numArray; 17 }
- beginChooseDishesAndIndexs用於開始隨機點菜
1 function beginChooseDishesAndIndexs(dishNumArray){ 2 var resultArray=[]; 3 var hasChoosedDishes=[]; // save be choosed dish 4 var hasChoosedIndexs=[]; // save be choosed in sourceArray index 5 var m = getRandom(dishNumArray.length); //random pos start 6 var dishLength=dishNumArray.length; 7 for(var i=0;i<dishLength;i++){ 8 var index = ((i+m)>=dishLength)?i+m-dishLength:(i+m); 9 var dishNum=dishNumArray[index]; 10 var tempSingleChoosed = []; // temp singleType choosed array 11 for(var n=0;n<dishNum;n++){ 12 var singleTypeArray = allDishArray[index]; 13 var singleTypeIndex = getRandom(singleTypeArray.length); 14 //alert(tempSingleChoosed+"and"+singleTypeIndex); 15 while(tempSingleChoosed.length <= singleTypeArray.length && checkIfInArray(tempSingleChoosed,singleTypeIndex)){ 16 singleTypeIndex = getRandom(singleTypeArray.length); // if now index is choosed,choose again 17 //alert("reGet"+singleTypeIndex); 18 } 19 if(tempSingleChoosed.length == singleTypeArray.length){ 20 continue; // if singleTypeDish all been choosed, beak this circle,to next type dish 21 } 22 hasChoosedDishes[hasChoosedDishes.length] = singleTypeArray[singleTypeIndex] 23 tempSingleChoosed[tempSingleChoosed.length] = singleTypeIndex; // ramark the temp position 24 hasChoosedIndexs[hasChoosedIndexs.length] = index+","+singleTypeIndex; // ramark the position 25 } 26 } // all dish has choosed 27 resultArray.push(hasChoosedDishes); 28 resultArray.push(hasChoosedIndexs); 29 return resultArray; 30 }
- sortChoosedArray用於排序
1 // when dishes been choosed ,sort it,from big to small 2 function sortChoosedArray(beenChoosedArray){ 3 var hasChoosedDishes=beenChoosedArray[0]; // save be choosed dish 4 var hasChoosedIndexs=beenChoosedArray[1]; // save be choosed in sourceArray index 5 for(var i=0;i<hasChoosedDishes.length;i++){ 6 for(var j=i;j<hasChoosedDishes.length;j++){ 7 if(getDishAmount(hasChoosedDishes[i])>getDishAmount(hasChoosedDishes[j])){ 8 var temp = hasChoosedDishes[i]; 9 hasChoosedDishes[i] = hasChoosedDishes[j]; 10 hasChoosedDishes[j] = temp; 11 // also should syn the choosedIndex 12 var temp2 = hasChoosedIndexs[i]; 13 hasChoosedIndexs[i] = hasChoosedIndexs[j]; 14 hasChoosedIndexs[j] = temp2; 15 } 16 } 17 } 18 //alert(hasChoosedDishes); 19 }
- checkAndChangeDishes用於對選擇的菜進行金額限制檢查
1 // check if over money ,change less cost dish 2 function checkAndChangeDishes(beenChoosedArray,hasPayedMoney,tempSumMoney){ 3 var outRange = moneyLimit?0:outRangeMoney; 4 while((hasPayedMoney+getSumArray(beenChoosedArray[0]))>tempSumMoney+outRange){ 5 if(getRandomRate(8)){ 6 changeOneToLessExpensive(beenChoosedArray);// random choose one dish then change it to less expensive 7 sortChoosedArray(beenChoosedArray); // reSort 8 }else{ 9 removeDish(beenChoosedArray); // remove the most or least Expensive dish 10 } 11 } 12 }
- showChooseResult用於將結果顯示到頁面上
1 // show the choose result 2 function showChooseResult(beenChoosedArray,hasPayedMoney,tempPeoples){ 3 var hasChoosedDishes=beenChoosedArray[0]; // save be choosed dish 4 var hasChoosedIndexs=beenChoosedArray[1]; // save be choosed in sourceArray index 5 var tempcoldMes="涼菜:",tempVegeMes="蔬菜:",tempMeatMes="肉菜:",tempSoupMes="湯:",tempRiceMes="主食:"; 6 for(var i in hasChoosedDishes){ 7 var choosedIndex = hasChoosedIndexs[i]; 8 var thisChoosedDish = hasChoosedDishes[i]; 9 var thisDishArray = thisChoosedDish.split("@"); 10 var allDishArrayIndex = (choosedIndex.split(","))[0]; 11 switch (allDishArrayIndex){ 12 case "0":tempMeatMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; 13 case "1":tempVegeMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; 14 case "2":tempcoldMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; 15 case "3":tempSoupMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; 16 case "4":tempRiceMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; 17 default:break; 18 } 19 hasPayedMoney += parseInt(thisDishArray[1]); 20 } 21 var resultMessage=""; 22 if(tempcoldMes.length>3){ 23 resultMessage += tempcoldMes.slice(0,-1)+"\n\n"; 24 } 25 if(tempVegeMes.length>3){ 26 resultMessage += tempVegeMes.slice(0,-1)+"\n\n"; 27 } 28 if(tempMeatMes.length>3){ 29 resultMessage += tempMeatMes.slice(0,-1)+"\n\n"; 30 } 31 if(tempSoupMes.length>2){ 32 resultMessage += tempSoupMes.slice(0,-1)+"\n\n"; 33 } 34 if(tempRiceMes.length>3){ 35 resultMessage += tempRiceMes.slice(0,-1)+"\n\n"; 36 }else if(eatRiceFlag){ 37 resultMessage += "主食:"+tempPeoples+"碗米飯("+eachRicePayed+"元/碗)"+"\n\n"; 38 } 39 resultMessage += "共花費"+hasPayedMoney+"元"+"\n"; 40 41 resultMes.innerText = resultMessage; 42 }
- 初始化默認參數:
其他都是一些輔助性的函數,見附件。
5.附件點擊下載: