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

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

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

    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方法开始点菜

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

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

万事不求人系列之-智能点餐算法实现-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. validator 参数校验的常用注解

    @AssertFalse Boolean,boolean 验证注解的元素值是false @AssertTrue Boolean,boolean 验证注解的元素值是true @NotNull 任意类型 ...

  2. 相机IMU融合四部曲(二):误差状态四元数详细解读

    相机IMU融合四部曲(二):误差状态四元数详细解读 极品巧克力 前言 上一篇文章,<D-LG-EKF详细解读>中,讲了理论上的SE3上相机和IMU融合的思想.但是,还没有涉及到实际的操作, ...

  3. Selenium自动化测试WebDriver下载

    1. 所有版本chrome下载 是不是很难找到老版本的chrome?博主收集了几个下载chrome老版本的网站,其中哪个下载的是原版的就不得而知了. http://www.slimjet.com/ch ...

  4. libpcap编程实例

    #include <stdio.h> #include <stdlib.h> #include <pcap.h> #include <errno.h> ...

  5. PHP发红包程序限制红包的大小

    我们先来分析下规律. 设定总金额为10元,有N个人随机领取: N=1 第一个 则红包金额=X元: N=2 第二个 为保证第二个红包可以正常发出,第一个红包金额=0.01至9.99之间的某个随机数. 第 ...

  6. boost::bind和boost::function使用示例

    C++11已支持bind和function,之前的不支持,但可以借助boost达到同样目的.看如下两段代码: 1) 创建HDFS目录 void hdfs::init() { if (0 == hdfs ...

  7. PreparedStatementSQLException

    目录 文章背景 目录 问题分析 问题解决 说明 参考文章 版本记录 layout: default title: PreparedStatementSQLException category: [Te ...

  8. JavaScript - this详解 (一)

    侃侃JavaScript中的this this为何如此多变? this总是跟它的执行上下文有关,而在JavaScript总会有开辟新的执行上下文的东西,比如函数,所以,this才如此的变化. 执行环境 ...

  9. pom.xml导入项目的时候,出错

  10. WebApi跨域请求

    在实际开发中 会有提供webapi给前端js 直接调用的情况, 这时候就会有存在跨域的情况, 解决方案: 在Global中添加代码 protected void Application_BeginRe ...