万事不求人系列之-智能点餐算法实现-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 money9     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)+"\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     }

      其他都是一些辅助性的函数,见附件。

5.附件点击下载

时间: 2024-08-01 02:31:35

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

算法系列(四)排序算法下篇--如何超越排序算法下界

概述 在算法系列(四)排序算法中篇--归并排序和快速排序一文中,我们介绍了归并排序和快速排序,最坏的情况下,最快的排序算法的时间复杂度是O(nlogn),是否有更好的算法呢?到目前为止,没有特殊的规则,O(nlogn)已经是最好的排序算法了,也就是说通用排序算法的时间复杂度下界就是O(nlogn).如果限定一些规则,是可以打破这个下界的.下面说一下尽在O(n)时间内就能实现对数组排序的算法. 基于排序的规则 基于什么样的规则才能突破排序的下界呢?我们需要分析一下排序消耗的时间.排序需要遍历,比较

二分查找算法的 JavaScript 实现

二分查找在查找[指定值]在[有序]数据中的[位置]时是一种高效的算法. 以下仅提供 ES5 版本. var arr = [0, 2, 4, 27, 28, 54, 67, 74, 75, 79, 86, 97, 289, 290, 678] function binarySearch(arr, val) { var start = 0, end = arr.length - 1; while (start <= end) { var mid = Math.floor((start + end)

冒泡排序算法的JavaScript实现

作为经典的排序算法之一,冒泡排序在JavaScript中也有多种实现方式. 在一些最常见的实现中会声明临时变量,在另一些不必声明临时变量的实现中则可能只支持数值数组或字符串数组(二者之一). 下面介绍两种[无须声明临时变量],[对数值数组和字符串数组都适用]的实现方式. ES5版: var arr = [5, 25, 2, 53, 22, 10]; function bubbleSort(arr) { for (var j = 1, len = arr.length; j < len; j++)

【JavaScript】【算法】JavaScript版排序算法

JavaScript版排序算法:冒泡排序.快速排序.插入排序.希尔排序(小数据时,希尔排序会比快排快哦) 1 //排序算法 2 window.onload = function(){ 3 var array = [0,1,2,44,4, 4 324,5,65,6,6, 5 34,4,5,6,2, 6 43,5,6,62,43, 7 5,1,4,51,56, 8 76,7,7,2,1, 9 45,4,6,7,8]; 10 //var array = [4,2,5,1,0,3]; 11 array

快速排序算法的 JavaScript 实现

快速排序是一种在大多数情况下比冒泡排序效率更高(详情参考有关算法复杂度的文章)的算法. 注意:许多编程语言内置的排序 API 底层实现便是基于快速排序. ES5 与 ES6 语法在实现该算法时区别不大,以下仅提供 ES5 版本. function quickSort(arr) { var len = arr.length; if (len <= 1) { return arr.slice(0); // 注意用 slice 可防范 arr[0] 为 undefined } var left = [

一些常见算法的JavaScript实现

在Web开发中,JavaScript很重要,算法也很重要.下面整理了一下一些常见的算法在JavaScript下的实现,包括二分法.求字符串长度.数组去重.插入排序.选择排序.希尔排序.快速排序.冒泡法等等.仅仅是为了练手,不保证高效与美观,或许还有Bug,有时间再完善吧.汝阳县第一中学 二分法: function binary(items,value){ var startIndex=0, stopIndex=items.length-1, midlleIndex=(startIndex+sto

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

点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate ASP.NET Boilerplate的js库提供了一些让javas

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

在前一次的发布的时候,我们共享了Visual Studio 2013中Windows Azure移动服务的集成和功能.其中包含了移动服务表脚本的编辑能力的介绍.这一次的发布,我们将描述在Visual Studio中怎么样在你的服务器脚本中激活智能提示.这个功能并没有被集成到Visual Studio 2013 预览版中,下边的步骤可以帮助你把它打开. 开始之前,请从这里下载我们的IntelliSense定义文件.将这些文件保存到一个相对容易访问的路径. 智能提示知多少? 我们今天将要发布的文件能

智能硬件时代来临,哪款智能手表是你的菜?

近两年来智能手表.智能手环等智能穿戴产品如雨后春笋般出现,如果说2014是智能手表的元年,那么2015年是会不会是井喷的一年呢?依目前情况趋势看,2015年智能穿戴产品市场用百家争鸣,争奇斗艳来形容一点也不为过.提到智能手表,可能有些人首先想到的就是Apple Watch,自2015年4月上市以来就引发了智能手表爱好的极大关注.但从2588到12万元不等的价钱让我等非土豪之辈望而却步. 自智能手表诞生以来,在这个流行"颠覆"概念的年代,它被很多人寄予颠覆传统手表开启智能可穿戴设备时代的