1.项目背景
最近遇到需要从经理批量分配订单到主管,主管批量分配订单到人员,我们只需要设计一种算法经理到主管(主管到人类似),尽量保证经理分配到主管的订单的平均金额与数据库里面的平均金额相等。我为此设计了两种算法如下,仅供参考。
2.算法以及java实现
2.1 算法1
2.1.1 算法思路
此算法是先传入所有组(一组对应一个主管)需要分配的订单数,按分配的订单数从大到小排序,比如A,B,C三组,订单数为5,3,1。再从数据库里获取一批数据按照订单金额从高到低排序到一个list。
比如数据库里订单金额及订单号为(a99,99)…(a1,1)。第一轮分配先取最大的订单金额的分配给A,即A拥有a99,B分配到a98,C分配到a97。第二轮分配最小值,A分配a1,B分配a2,第三轮分配最大值,即A分配a96,B分配a95,以此类推。
2.1.2 算法实现如下
int unallocated = collectionService.findAllUnabsorbedCollectionCount(collectionVo);
int assignCount = 0;
for(GroupInfoRecord groupInfoRecord : req.getGroupInfoRecord()){
assignCount += groupInfoRecord.getAssignCount(); //获取总分配数
}
if(unallocated < assignCount ){
return BusRsp.faild(Constant.CODE_FAILD, "经理未分配总数:"+unallocated+" 需要分配数量之和:"+assignCount+";超出分配数量");
}
collectionVo.setCollectionNum(assignCount);
//部门分配到组
// 查出分配的订单id
List<CollectionPo> collectionIdList = collectionService.findGroupAssignCollectionId(collectionVo);
LinkedList<CollectionPo> linkedList = new LinkedList(collectionIdList); //把list转为LinkedList,方便对头尾进行操作
int listSize = linkedList.size();
//先把组进行排序,按照订单量从高到低排序
Map<String,Integer> map = new TreeMap<>(); //key:人员id value: 分配订单数
for(GroupInfoRecord groupInfoRecord : req.getGroupInfoRecord()){
map.put(groupInfoRecord.getGroupId(),groupInfoRecord.getAssignCount());
}
//将我们得到的map按照金额降序排列
// 将map.entrySet()转换成list,该list已经按照订单数从大到小排序
List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
//降序排序
Collections.sort(list, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));//这里可转为lambda表达式
// requestNum 代表页面提交的数目 requestGet表示数组当前已经变化的数值 groupIds存放的是requestNum数目对应的人员id
int[] requestNum = new int[map.size()];
int[] requestGet = new int[map.size()];
String[] groupIds = new String[map.size()];
int j = 0;
//给人员id以及对应的数目赋值
for (Map.Entry<String, Integer> mapping : list) {
System.out.println("key:"+mapping.getKey() + " value: " + mapping.getValue());
groupIds[j] = mapping.getKey();
requestNum[j] = mapping.getValue();
j++;
}
boolean[] requestGetFlag = new boolean[map.size()]; //初始化代表每个组的值都为true,如果该组数目满足要求,则置为false,下一轮不再分配
int index = 0; //代表在某一轮分配中分配给第index+1组
for (int i = 0; i < map.size(); i++){
requestGetFlag[i] = true;
}
boolean flag;
int odd = 0;
while (linkedList.size() > 0 && assignCount > 0 ){
flag = true;
for (int i = 0; i < map.size(); i++){
if ( 0 == index){ //代表某一轮分配完毕,开始分配下一轮
odd = (odd + 1) % 2; //奇数轮分配最大的,偶数轮分配最小的
}
if(requestGetFlag[index] && flag){
assignCount--; //分配一个自减1
requestGet[index]++;
//取出订单列表的第一个,直接去数据库更新,此时更新的是map中的第一个,之后变成第二个,第三个
//index+1 代表是第几个groupIds[index+1] linkedList.get(0)表示获取订单list的第一个 分配给第 index + 1个
//针对此订单更新数据库,把该订单分配给groupIds[index+1]
//判断订单状态是否已经完成
CollectionPo po = null;
int collectionNo = 0 ;
if (odd == 1){
collectionNo = 0;
} else {
collectionNo = linkedList.size()-1 ;
}
po = collectionService.getById(linkedList.get(collectionNo).getCollectionId());
System.out.println("\n\n*************\n此次分配的订单id为:" + linkedList.get(collectionNo).getCollectionId() + "\n分配给:" + groupIds[index] + "\n***********\n\n");
if(odd == 1){
linkedList.removeFirst();
} else{
linkedList.removeLast();
}
flag = false;
if (requestGet[index] == requestNum[index])
requestGetFlag[index] = false;
index ++;
if(index > map.size() - 1){
index = 0;
}
break;
}
index ++;
if(index > map.size() - 1){
index = 0;
}
}
}
2.1.3 算法的优势与弊端
此算法算是比较简单的一类,一个萝卜一个坑往里填,但是实现起来还是很复杂。对于数据库中存有的数据离散性有一定要求,最好是订单金额的中位数等于或接近平均数。