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     }
           其他都是一些辅助性的函数,见附件。
     
  • 相关阅读:
    获得目标服务器中所有数据库名、表名、列名
    SQL Server 2008 安装SQLDMO.dll
    三层交换原理
    NAT地址转换原理全攻略
    C#中显/隐式实现接口及其访问方法
    As,is含义?using 语句
    c#泛型约束
    C#几个经常犯错误汇总
    C#--深入分析委托与事件
    markdown基础
  • 原文地址:https://www.cnblogs.com/dimmacro/p/4466668.html
Copyright © 2011-2022 走看看