作为一个成长中的架构师,编码能力是万不能停止的,这个算法是之前在上一家单位帮助同事们自助订餐写的,纯爱好自己码敲的,刚好这段时间重新整理代码,发现了它,分享给大家,请大家品评指教。

  1. 使用场景介绍:随着各种订餐APP的出现,找饭馆团购券,为自己订点好吃的或者团购固定人数的券都很方便,但是很多时候我们遇到的是这样的场景:知道用餐人数是几人,但是总经费或人均消费标准有限制,让你点菜,你就得一方面考虑荤素搭配,有菜有汤有主食,另一方面还得考虑经费限制;还有一种情况是就这么多总经费,人数又不确定(如之前答应去临走又有事告假的),人少可以多点几个硬菜,人多只能综合考虑,拿主食顶上。这两种场景,对点菜的人提出了很高的要求,本算法就是针对此种情况,只要给出用餐人数或固定金额,自动为你科学点菜,妈妈再也不用担心你是个点菜盲。
  2. 基本效果截图:

        如上图所示,输入人数点击开始点菜,系统自动会为你按照人均20的标准给出合理的菜单,这个人均标准是系统默认设置的,可以调整参数。如果输入人数的同时输入限定金额,则会以此金额为总花费的参考,保证不超过此金额下最优的给出建议菜单。当然如果同时输入了人数和限定金额,那么限定金额/人数不能低于系统设置的人均最低值,比如人数6人,限定金额50,人均很不到10块,下个毛管子。
    3.基本原理:根据人数或限定金额得到本次菜单的可用总金额,同时根据人数按照一定荤素比例计算各类菜需要的个数,如6个人需要三个肉菜,一个蔬菜,一个凉菜,6分主食。(这个菜个数的计算是随机的,凉菜随机出现,主食可以是米饭也可以是饺子之类的,这个也是随机的。同时蔬菜和肉菜的比例虽然固定,但是每次随机会有小的调整,有上下浮动。);得到每类菜的个数后开始从对应类别中随机选择,得到结果后按照金额的限制先排序再进行适当剔除或重选,使得总限定金额最优化,最后得到菜单并输出。
    4.核心JS函数解释说明:
    1. 初始化默认参数:

           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 = []; // 饭店所有的菜品
      
      

      基本参数有:荤蔬搭配比例、人均最少消费、默认人均消费、要米饭还是其他主食、要米饭的概率、每碗米饭的价钱、是否有总消费限制、上下浮动的空间、饭店所有的菜品(这个需要初始化,将菜品按荤蔬凉菜汤米饭等类别分开,具体代码没有贴上来,看附件里)

    2. 点击“开始点菜"执行的方式解释:
           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方法开始点菜

    3. 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用于将结果显示到页面上。下面是具体每个函数的源码,有注释。

    4. 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;
      }
    5. 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;
      }
    6. 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;
      }
    7. 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);
      }
    8. 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
      }
      }
      }
    9. 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;
      }
       其他都是一些辅助性的函数,见附件。
 

万事不求人系列之-智能点餐算法实现-JavaScript实现智能点餐的更多相关文章

  1. 分布式理论系列(二)一致性算法:2PC 到 3PC 到 Paxos 到 Raft 到 Zab

    分布式理论系列(二)一致性算法:2PC 到 3PC 到 Paxos 到 Raft 到 Zab 本文介绍一致性算法: 2PC 到 3PC 到 Paxos 到 Raft 到 Zab 两类一致性算法(操作原 ...

  2. ABP(现代ASP.NET样板开发框架)系列之21、ABP展现层——Javascript函数库

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project ...

  3. 转载部长一篇大作:常用排序算法之JavaScript实现

    转载部长一篇大作:常用排序算法之JavaScript实现 注:本文是转载实验室同门王部长的大作,找实习找工作在即,本文颇有用处!原文出处:http://www.cnblogs.com/ywang172 ...

  4. Visual Studio 2013开启JavaScript的智能提示功能

    在前一次的发布的时候,我们共享了Visual Studio 2013中Windows Azure移动服务的集成和功能.其中包含了移动服务表脚本的编辑能力的介绍.这一次的发布,我们将描述在Visual ...

  5. JavaScript 排序算法(JavaScript sorting algorithms)

    JavaScrip 排序算法(JavaScript Sorting Algorithms) 基础构造函数 以下几种排序算法做为方法放在构造函数里. function ArrayList () { va ...

  6. webpack4 系列教程(十二):处理第三方JavaScript库

    教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十二):处理第三方 JavaScript 库>原文地址.或者来我的小站看更多内容:godbm ...

  7. 十大经典排序算法的 JavaScript 实现

    计算机领域的都多少掌握一点算法知识,其中排序算法是<数据结构与算法>中最基本的算法之一.排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大 ...

  8. 【HANA系列】SAP HANA XS使用服务器JavaScript Libraries详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS使用服务器 ...

  9. .Net Core ORM选择之路,哪个才适合你 通用查询类封装之Mongodb篇 Snowflake(雪花算法)的JavaScript实现 【开发记录】如何在B/S项目中使用中国天气的实时天气功能 【开发记录】微信小游戏开发入门——俄罗斯方块

    .Net Core ORM选择之路,哪个才适合你   因为老板的一句话公司项目需要迁移到.Net Core ,但是以前同事用的ORM不支持.Net Core 开发过程也遇到了各种坑,插入条数多了也特别 ...

随机推荐

  1. ThreadLocal原理深入解析

    目录 1. 从一次项目经历说起 2. ThreadLocal源码解析 2.1 set方法源码解析 2.2 get方法源码解析 2.3 ThreadLocal源码总结 3. ThreadLocalMap ...

  2. Finite State Transducers

    一, 简介 Finite State Transducers 简称 FST, 中文名:有穷状态转换器.在自然语言处理等领域有很大应用,其功能类似于字典的功能(STL 中的map,C# 中的Dictio ...

  3. Echarts主题颜色

    Echarts主题颜色搜集: 直接覆盖默认颜色即可 例如在 echarts.setOption({ '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80', ...

  4. css中calc()的使用

    calc()是css3中新出现的特性,可以用于动态计算,非常方便. 首先是兼容性 再来看看怎么使用 html{ font-size: 20px; } div{ width: calc(50% - 1p ...

  5. jdk1.7 环境变量配置

    Windows系统中设置环境变量如下图右击“我的电脑”,选择“属性”. 点击“高级”选项卡,选择“环境变量”.  在“系统环境变量”中设置上面提到的3个环境变量,如果变量已经存在就选择“编辑”,否则选 ...

  6. osgQt支持触摸屏

    1. osgQt的构造函数添加:setAttribute(Qt::WA_AcceptTouchEvents);//wyh 2. event()修改,支持触摸时间 bool GLWidget::even ...

  7. javascript总结23:javascript 数据类型与变量

    1  基本类型和引用类型 JavaScript中的数据类型分为两类:基本类型和引用类型 基本类型:直接存储值,画图解释 Number.String.Boolean Undefined.Null 引用类 ...

  8. 白话浅说TCP/UDP面向连接,面向无连接的区别

    TCP是面向连接的UDP是面向无连接的就是这种关系了 TCP(Transmission Control Protocol,传输控制协议) UDP(User Datagram Protocol,用户数据 ...

  9. C# 时间戳的生成

    /**        * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数         * @return 时间戳        */        publi ...

  10. 使用word文档直接发表博客 8 )

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...