万事不求人系列之-智能点餐算法实现-JavaScript实现智能点餐
2015-04-29 17:59
357 查看
作为一个成长中的架构师,编码能力是万不能停止的,这个算法是之前在上一家单位帮助同事们自助订餐写的,纯爱好自己码敲的,刚好这段时间重新整理代码,发现了它,分享给大家,请大家品评指教。
使用场景介绍:随着各种订餐APP的出现,找饭馆团购券,为自己订点好吃的或者团购固定人数的券都很方便,但是很多时候我们遇到的是这样的场景:知道用餐人数是几人,但是总经费或人均消费标准有限制,让你点菜,你就得一方面考虑荤素搭配,有菜有汤有主食,另一方面还得考虑经费限制;还有一种情况是就这么多总经费,人数又不确定(如之前答应去临走又有事告假的),人少可以多点几个硬菜,人多只能综合考虑,拿主食顶上。这两种场景,对点菜的人提出了很高的要求,本算法就是针对此种情况,只要给出用餐人数或固定金额,自动为你科学点菜,妈妈再也不用担心你是个点菜盲。
基本效果截图:
如上图所示,输入人数点击开始点菜,系统自动会为你按照人均20的标准给出合理的菜单,这个人均标准是系统默认设置的,可以调整参数。如果输入人数的同时输入限定金额,则会以此金额为总花费的参考,保证不超过此金额下最优的给出建议菜单。当然如果同时输入了人数和限定金额,那么限定金额/人数不能低于系统设置的人均最低值,比如人数6人,限定金额50,人均很不到10块,下个毛管子。
3.基本原理:根据人数或限定金额得到本次菜单的可用总金额,同时根据人数按照一定荤素比例计算各类菜需要的个数,如6个人需要三个肉菜,一个蔬菜,一个凉菜,6分主食。(这个菜个数的计算是随机的,凉菜随机出现,主食可以是米饭也可以是饺子之类的,这个也是随机的。同时蔬菜和肉菜的比例虽然固定,但是每次随机会有小的调整,有上下浮动。);得到每类菜的个数后开始从对应类别中随机选择,得到结果后按照金额的限制先排序再进行适当剔除或重选,使得总限定金额最优化,最后得到菜单并输出。
4.核心JS函数解释说明:
初始化默认参数:
基本参数有:荤蔬搭配比例、人均最少消费、默认人均消费、要米饭还是其他主食、要米饭的概率、每碗米饭的价钱、是否有总消费限制、上下浮动的空间、饭店所有的菜品(这个需要初始化,将菜品按荤蔬凉菜汤米饭等类别分开,具体代码没有贴上来,看附件里)
点击“开始点菜"执行的方式解释:
做了一些基本的输入有效性验证,比如人数不能为空,输入格式校验等,然后进入randomChooseDish方法开始点菜
确定人数和总金额,checkCondition做基本的条件判断,比如人数不能少于2人,总金额/人数不能低于人均最低值等;getDishNumArray用于根据人数和初始化荤素比例计算每类菜品需要点的数量;beginChooseDishesAndIndexs用于开始随机点菜;sortChoosedArray用于排序,从贵到便宜,这样对于便宜的菜可以有更多搭配的方式;checkAndChangeDishes用于对选择的菜进行金额限制检查,如果超过限制则开始从最便宜的菜调整菜,直到菜单合格;showChooseResult用于将结果显示到页面上。下面是具体每个函数的源码,有注释。
checkCondition做基本的条件判断
getDishNumArray用于根据人数和初始化荤素比例计算每类菜品需要点的数量
beginChooseDishesAndIndexs用于开始随机点菜
sortChoosedArray用于排序
checkAndChangeDishes用于对选择的菜进行金额限制检查
showChooseResult用于将结果显示到页面上
其他都是一些辅助性的函数,见附件。
5.附件点击下载:
使用场景介绍:随着各种订餐APP的出现,找饭馆团购券,为自己订点好吃的或者团购固定人数的券都很方便,但是很多时候我们遇到的是这样的场景:知道用餐人数是几人,但是总经费或人均消费标准有限制,让你点菜,你就得一方面考虑荤素搭配,有菜有汤有主食,另一方面还得考虑经费限制;还有一种情况是就这么多总经费,人数又不确定(如之前答应去临走又有事告假的),人少可以多点几个硬菜,人多只能综合考虑,拿主食顶上。这两种场景,对点菜的人提出了很高的要求,本算法就是针对此种情况,只要给出用餐人数或固定金额,自动为你科学点菜,妈妈再也不用担心你是个点菜盲。
基本效果截图:
如上图所示,输入人数点击开始点菜,系统自动会为你按照人均20的标准给出合理的菜单,这个人均标准是系统默认设置的,可以调整参数。如果输入人数的同时输入限定金额,则会以此金额为总花费的参考,保证不超过此金额下最优的给出建议菜单。当然如果同时输入了人数和限定金额,那么限定金额/人数不能低于系统设置的人均最低值,比如人数6人,限定金额50,人均很不到10块,下个毛管子。
3.基本原理:根据人数或限定金额得到本次菜单的可用总金额,同时根据人数按照一定荤素比例计算各类菜需要的个数,如6个人需要三个肉菜,一个蔬菜,一个凉菜,6分主食。(这个菜个数的计算是随机的,凉菜随机出现,主食可以是米饭也可以是饺子之类的,这个也是随机的。同时蔬菜和肉菜的比例虽然固定,但是每次随机会有小的调整,有上下浮动。);得到每类菜的个数后开始从对应类别中随机选择,得到结果后按照金额的限制先排序再进行适当剔除或重选,使得总限定金额最优化,最后得到菜单并输出。
4.核心JS函数解释说明:
初始化默认参数:
var dishRate=[0.4,0.5,0.1]; // meat,vege,cold var leastAveragePayed = 10; // Average consumption money var defaultAveragePayed = 20; // default consumption money var eatRiceFlag = true; // if eat rice or other things var eatRiceRate = 8; // the rate people eat rice; var eachRicePayed = 3; // each rice cost how much var moneyLimit = false; var outRangeMoney = 5; // can over money 9 var allDishArray = []; // 饭店所有的菜品
基本参数有:荤蔬搭配比例、人均最少消费、默认人均消费、要米饭还是其他主食、要米饭的概率、每碗米饭的价钱、是否有总消费限制、上下浮动的空间、饭店所有的菜品(这个需要初始化,将菜品按荤蔬凉菜汤米饭等类别分开,具体代码没有贴上来,看附件里)
点击“开始点菜"执行的方式解释:
function execute(){ var peoples=eatPeoples.value; var money=payMoney.value; if("" == peoples){ resultMes.innerText = "请输入用餐人数!"; return; } if(!/^\d+$/.test(peoples) || (("" != money) && !/^\d+$/.test(money))){ resultMes.innerText = "输入格式不对,请重新输入!"; return; } if(""!=money.replace(/[\s]+/g,"")){ moneyLimit = true; } randomChooseDish(peoples,money); }
做了一些基本的输入有效性验证,比如人数不能为空,输入格式校验等,然后进入randomChooseDish方法开始点菜
randomChooseDish方法如下:
function randomChooseDish(peoples,money){ var tempPeoples=parseInt(peoples); var tempSumMoney= (""==money)?tempPeoples*parseInt(defaultAveragePayed):parseInt(money); if(!checkCondition(tempPeoples,tempSumMoney)){ return; } var dishNumArray= getDishNumArray(tempPeoples); //get dishNumArray var hasPayedMoney=0; if(eatRiceFlag){ // eat rice,reduce the rice money hasPayedMoney = eachRicePayed*tempPeoples; } var beenChoosedArray = beginChooseDishesAndIndexs(dishNumArray); sortChoosedArray(beenChoosedArray); // when dishes are been choosed ,should check checkAndChangeDishes(beenChoosedArray,hasPayedMoney,tempSumMoney); // show result showChooseResult(beenChoosedArray,hasPayedMoney,tempPeoples); }
确定人数和总金额,checkCondition做基本的条件判断,比如人数不能少于2人,总金额/人数不能低于人均最低值等;getDishNumArray用于根据人数和初始化荤素比例计算每类菜品需要点的数量;beginChooseDishesAndIndexs用于开始随机点菜;sortChoosedArray用于排序,从贵到便宜,这样对于便宜的菜可以有更多搭配的方式;checkAndChangeDishes用于对选择的菜进行金额限制检查,如果超过限制则开始从最便宜的菜调整菜,直到菜单合格;showChooseResult用于将结果显示到页面上。下面是具体每个函数的源码,有注释。
checkCondition做基本的条件判断
function checkCondition(tempPeoples,tempSumMoney){ if(tempPeoples<2){ //alert(); resultMes.innerText = "一个人下馆子?太奢侈了."; return false; } if(tempPeoples>25){ //alert(); resultMes.innerText = "人数太多,一桌坐不下!"; return false; } if(tempSumMoney<tempPeoples*leastAveragePayed){ //alert(); resultMes.innerText ="太抠了吧,都不到人均消费10块!"; return false; } return true; }
getDishNumArray用于根据人数和初始化荤素比例计算每类菜品需要点的数量
// get meat,vege,cold numArray function getDishNumArray(tempPeoples){ 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 if(getSumArray(numArray)<=tempPeoples+1 || tempPeoples>=10){ var soupNum = Math.floor(tempPeoples/4) numArray[numArray.length]=soupNum>2?2:soupNum; // add soup,soup num small then 2 } eatRiceFlag = getRandomRate(eatRiceRate); if(!eatRiceFlag){ // eat others var mainRiceNum = Math.floor(tempPeoples/3); numArray[numArray.length]=mainRiceNum>5?5:mainRiceNum; // add rice, mainrice nums small then 5 } return numArray; }
beginChooseDishesAndIndexs用于开始随机点菜
function beginChooseDishesAndIndexs(dishNumArray){ var resultArray=[]; var hasChoosedDishes=[]; // save be choosed dish var hasChoosedIndexs=[]; // save be choosed in sourceArray index var m = getRandom(dishNumArray.length); //random pos start var dishLength=dishNumArray.length; for(var i=0;i<dishLength;i++){ var index = ((i+m)>=dishLength)?i+m-dishLength:(i+m); var dishNum=dishNumArray[index]; var tempSingleChoosed = []; // temp singleType choosed array for(var n=0;n<dishNum;n++){ var singleTypeArray = allDishArray[index]; var singleTypeIndex = getRandom(singleTypeArray.length); //alert(tempSingleChoosed+"and"+singleTypeIndex); while(tempSingleChoosed.length <= singleTypeArray.length && checkIfInArray(tempSingleChoosed,singleTypeIndex)){ singleTypeIndex = getRandom(singleTypeArray.length); // if now index is choosed,choose again //alert("reGet"+singleTypeIndex); } if(tempSingleChoosed.length == singleTypeArray.length){ continue; // if singleTypeDish all been choosed, beak this circle,to next type dish } hasChoosedDishes[hasChoosedDishes.length] = singleTypeArray[singleTypeIndex] tempSingleChoosed[tempSingleChoosed.length] = singleTypeIndex; // ramark the temp position hasChoosedIndexs[hasChoosedIndexs.length] = index+","+singleTypeIndex; // ramark the position } } // all dish has choosed resultArray.push(hasChoosedDishes); resultArray.push(hasChoosedIndexs); return resultArray; }
sortChoosedArray用于排序
// when dishes been choosed ,sort it,from big to small function sortChoosedArray(beenChoosedArray){ var hasChoosedDishes=beenChoosedArray[0]; // save be choosed dish var hasChoosedIndexs=beenChoosedArray[1]; // save be choosed in sourceArray index for(var i=0;i<hasChoosedDishes.length;i++){ for(var j=i;j<hasChoosedDishes.length;j++){ if(getDishAmount(hasChoosedDishes[i])>getDishAmount(hasChoosedDishes[j])){ var temp = hasChoosedDishes[i]; hasChoosedDishes[i] = hasChoosedDishes[j]; hasChoosedDishes[j] = temp; // also should syn the choosedIndex var temp2 = hasChoosedIndexs[i]; hasChoosedIndexs[i] = hasChoosedIndexs[j]; hasChoosedIndexs[j] = temp2; } } } //alert(hasChoosedDishes); }
checkAndChangeDishes用于对选择的菜进行金额限制检查
// check if over money ,change less cost dish function checkAndChangeDishes(beenChoosedArray,hasPayedMoney,tempSumMoney){ var outRange = moneyLimit?0:outRangeMoney; while((hasPayedMoney+getSumArray(beenChoosedArray[0]))>tempSumMoney+outRange){ if(getRandomRate(8)){ changeOneToLessExpensive(beenChoosedArray);// random choose one dish then change it to less expensive sortChoosedArray(beenChoosedArray); // reSort }else{ removeDish(beenChoosedArray); // remove the most or least Expensive dish } } }
showChooseResult用于将结果显示到页面上
// show the choose result function showChooseResult(beenChoosedArray,hasPayedMoney,tempPeoples){ var hasChoosedDishes=beenChoosedArray[0]; // save be choosed dish var hasChoosedIndexs=beenChoosedArray[1]; // save be choosed in sourceArray index var tempcoldMes="凉菜:",tempVegeMes="蔬菜:",tempMeatMes="肉菜:",tempSoupMes="汤:",tempRiceMes="主食:"; for(var i in hasChoosedDishes){ var choosedIndex = hasChoosedIndexs[i]; var thisChoosedDish = hasChoosedDishes[i]; var thisDishArray = thisChoosedDish.split("@"); var allDishArrayIndex = (choosedIndex.split(","))[0]; switch (allDishArrayIndex){ case "0":tempMeatMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; case "1":tempVegeMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; case "2":tempcoldMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; case "3":tempSoupMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; case "4":tempRiceMes += thisDishArray[0]+":"+thisDishArray[1]+",";break; default:break; } hasPayedMoney += parseInt(thisDishArray[1]); } var resultMessage=""; if(tempcoldMes.length>3){ resultMessage += tempcoldMes.slice(0,-1)+"\n\n"; } if(tempVegeMes.length>3){ resultMessage += tempVegeMes.slice(0,-1)+"\n\n"; } if(tempMeatMes.length>3){ resultMessage += tempMeatMes.slice(0,-1)+"\n\n"; } if(tempSoupMes.length>2){ resultMessage += tempSoupMes.slice(0,-1)+"\n\n"; } if(tempRiceMes.length>3){ resultMessage += tempRiceMes.slice(0,-1)+"\n\n"; }else if(eatRiceFlag){ resultMessage += "主食:"+tempPeoples+"碗米饭("+eachRicePayed+"元/碗)"+"\n\n"; } resultMessage += "共花费"+hasPayedMoney+"元"+"\n"; resultMes.innerText = resultMessage; }
其他都是一些辅助性的函数,见附件。
5.附件点击下载:
相关文章推荐
- 算法系列:PageRank算法的MapReduce实现
- 算法系列(一)背包、队列和栈(Java实现)
- 算法系列之-栈实现栈的排序
- 使用 Sublime Text 做 Javascript 编辑器 - 集成 SublimeCodeIntel 实现代码智能提示及自动完成
- 白话经典算法系列之三 希尔排序的实现
- 一种智能车控制算法的设计和实现
- 经典算法研究系列:九之续、sift算法的编译与实现
- Javascript与数据结构系列(二)——队列的实现
- VS2010与.NET4系列 20.VS2010的JavaScript智能感知增强
- 白话经典算法系列之中的一个 冒泡排序的三种实现
- 白话经典算法系列之中的一个 冒泡排序的三种实现
- 【转】数据挖掘系列(10)——卷积神经网络算法的一个实现
- 白话经典算法系列之五 归并排序的实现
- JavaScript学习系列之深入原型链与继承的实现
- JavaScript实现的一个计算数字步数的算法分享
- 白话经典算法系列之二 直接插入排序的三种实现
- 【转载】白话经典算法系列之四 直接选择排序及交换二个数据的正确实现
- 前端学习总结(二十二)——常见数据结构与算法javascript实现
- 白话经典算法系列之三 希尔排序的实现
- 白话经典算法系列之五 归并排序的实现