萬事不求人系列之-智能點餐算法實現-JavaScript實現智能點餐


作為一個成長中的架構師,編碼能力是萬不能停止的,這個算法是之前在上一家單位幫助同事們自助訂餐寫的,純愛好自己碼敲的,剛好這段時間重新整理代碼,發現了它,分享給大家,請大家品評指教。

  1. 使用場景介紹:隨着各種訂餐APP的出現,找飯館團購券,為自己訂點好吃的或者團購固定人數的券都很方便,但是很多時候我們遇到的是這樣的場景:知道用餐人數是幾人,但是總經費或人均消費標准有限制,讓你點菜,你就得一方面考慮葷素搭配,有菜有湯有主食,另一方面還得考慮經費限制;還有一種情況是就這么多總經費,人數又不確定(如之前答應去臨走又有事告假的),人少可以多點幾個硬菜,人多只能綜合考慮,拿主食頂上。這兩種場景,對點菜的人提出了很高的要求,本算法就是針對此種情況,只要給出用餐人數或固定金額,自動為你科學點菜,媽媽再也不用擔心你是個點菜盲。
  2. 基本效果截圖:

        

         如上圖所示,輸入人數點擊開始點菜,系統自動會為你按照人均20的標准給出合理的菜單,這個人均標准是系統默認設置的,可以調整參數。如果輸入人數的同時輸入限定金額,則會以此金額為總花費的參考,保證不超過此金額下最優的給出建議菜單。當然如果同時輸入了人數和限定金額,那么限定金額/人數不能低於系統設置的人均最低值,比如人數6人,限定金額50,人均很不到10塊,下個毛管子。
    3.基本原理:根據人數或限定金額得到本次菜單的可用總金額,同時根據人數按照一定葷素比例計算各類菜需要的個數,如6個人需要三個肉菜,一個蔬菜,一個涼菜,6分主食。(這個菜個數的計算是隨機的,涼菜隨機出現,主食可以是米飯也可以是餃子之類的,這個也是隨機的。同時蔬菜和肉菜的比例雖然固定,但是每次隨機會有小的調整,有上下浮動。);得到每類菜的個數后開始從對應類別中隨機選擇,得到結果后按照金額的限制先排序再進行適當剔除或重選,使得總限定金額最優化,最后得到菜單並輸出。
    4.核心JS函數解釋說明:
    1. 初始化默認參數:
      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 = []; // 飯店所有的菜品
       
                   

      基本參數有:葷蔬搭配比例、人均最少消費、默認人均消費、要米飯還是其他主食、要米飯的概率、每碗米飯的價錢、是否有總消費限制、上下浮動的空間、飯店所有的菜品(這個需要初始化,將菜品按葷蔬涼菜湯米飯等類別分開,具體代碼沒有貼上來,看附件里)

    2. 點擊“開始點菜"執行的方式解釋:
       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方法開始點菜

    3. 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用於將結果顯示到頁面上。下面是具體每個函數的源碼,有注釋。

    4. 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     }

       

    5. 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     }

       

    6. 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     }

       

    7. 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     }

       

    8. 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         }

       

    9. 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.附件點擊下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM