zoukankan      html  css  js  c++  java
  • 万事不求人系列之-智能点餐算法实现-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)+"
        
        ";
        24         }
        25         if(tempVegeMes.length>3){
        26             resultMessage += tempVegeMes.slice(0,-1)+"
        
        ";
        27         }
        28         if(tempMeatMes.length>3){
        29             resultMessage += tempMeatMes.slice(0,-1)+"
        
        ";
        30         }
        31         if(tempSoupMes.length>2){
        32             resultMessage += tempSoupMes.slice(0,-1)+"
        
        ";
        33         }
        34         if(tempRiceMes.length>3){
        35             resultMessage += tempRiceMes.slice(0,-1)+"
        
        ";
        36         }else if(eatRiceFlag){
        37             resultMessage += "主食:"+tempPeoples+"碗米饭("+eachRicePayed+"元/碗)"+"
        
        ";
        38         }
        39         resultMessage += "共花费"+hasPayedMoney+"元"+"
        ";
        40 
        41         resultMes.innerText = resultMessage;
        42     }
           其他都是一些辅助性的函数,见附件。
     
  • 相关阅读:
    基本MVVM 和 ICommand用法举例(转)
    WPF C# 命令的运行机制
    628. Maximum Product of Three Numbers
    605. Can Place Flowers
    581. Shortest Unsorted Continuous Subarray
    152. Maximum Product Subarray
    216. Combination Sum III
    448. Find All Numbers Disappeared in an Array
    268. Missing Number
    414. Third Maximum Number
  • 原文地址:https://www.cnblogs.com/dimmacro/p/4466668.html
Copyright © 2011-2022 走看看