递归算法实践--到仓合单助力京东物流提效增收
一、背景
京东物流到仓业务「对商家」为了减少商家按照京东采购单分货备货过程,对齐行业直接按照流向交接,提升商家满意度;「对京东」揽收操作APP提效;到仓合单功能应运而生;
二、问题
一次批量采购单(一次50或者100个采购单)需要根据不同的规则合并成多个订单;
每一个采购单可以是不同的来源类型(自营和非自营)、不同的收货类型,每一个采购单会有多个SKU,同一个SKU只有一个等级,一批采购单会有多个SKU,同一个SKU会有多个等级;
合单规则:
三、打法
A、思路
B、算法
递归算法是一种通过重复将问题分解为同类的子问题来解决问题的方法; 特点是函数或子程序在运行过程中直接或间接调用自身;常见的递归算法包括Fibonacci函数、Hanoi问题和阶乘计算等;
C、解决方案
1. 递归代码块
/**
* 指定不同等级不能合单
*
* @param poNoSet 采购单号Set
* @param poMainInfoMap 采购单详情
* @param calculatedSet 计算过的采购单据列表的集合
* @return
*/
private List<Set<String>> doMergeClassDifferent(Set<String> poNoSet, Map<String, PoOrderFacadeResponse.PoMainInfo> poMainInfoMap, Set<String> calculatedSet) {
// 如果该set已经计算过则不重复计算
List<Set<String>> resultList = new ArrayList<>();
String calculatedPoNoKey = buildCalculatedPoNoKey(poNoSet);
if (calculatedSet.contains(calculatedPoNoKey)) {
return resultList;
} else {
calculatedSet.add(calculatedPoNoKey);
resultValue.incrementAndGet();
}
// 以sku为key的集合
Set<String> skuSet = new HashSet<>();
// 以sku 为key, 值为poNos
Map<String, Set<String>> skuMap = new HashMap<>();
// 存放同一个sku下有多少个不同等级的集合
Map<String, Set<String>> skuToskuLevelMap = new HashMap<>();
// 以sku+level 为key的集合
Set<String> skuLevelSet = new HashSet<>();
// 以sku+level 为key, 值为poNos
Map<String, Set<String>> skuLevelMap = new HashMap<>();
for (String poNo : poNoSet) {
PoOrderFacadeResponse.PoMainInfo poMainInfo = poMainInfoMap.get(poNo);
// 采购单条目
List<PoOrderFacadeResponse.PoItemInfo> poItemInfos = poMainInfo.getPoItemInfos();
for (PoOrderFacadeResponse.PoItemInfo poItemInfo : poItemInfos) {
String skuKey = poItemInfo.getGoodsNo();
String skuLevelKey = buildSkuLevelKey(poItemInfo);
skuSet.add(skuKey);
setKeyMap(skuKey, skuMap, poNo);
// 存放同一个sku下有多少个不同等级的集合
Set<String> stringSet = skuToskuLevelMap.get(skuKey);
if (CollectionUtils.isEmpty(stringSet)) {
stringSet = new HashSet<>();
skuToskuLevelMap.put(skuKey, stringSet);
}
stringSet.add(skuLevelKey);
skuLevelSet.add(skuLevelKey);
setKeyMap(skuLevelKey, skuLevelMap, poNo);
}
}
if (skuSet.size() == skuLevelSet.size()) {
// 此处sku的数量和sku+level的数量相同,不需要再进行递归运算
// 方法结束的出口
resultList.add(poNoSet);
return resultList;
} else {
// 同一个sku下最多等级个数
int high = MagicCommonConstants.NUM_1;
// 最多等级个数的对应sku
String maxLevelSku = "";
for (String sku : skuToskuLevelMap.keySet()) {
Set<String> strings = skuToskuLevelMap.get(sku);
if (strings.size() > high) {
high = strings.size();
maxLevelSku = sku;
}
}
if (high > MagicCommonConstants.NUM_1) {
// 获取该sku下的poNos
Set<String> strings = skuMap.get(maxLevelSku);
// 差集
Set<String> chaJiSet = poNoSet;
chaJiSet.removeAll(strings);
Set<String> skuLevels = skuToskuLevelMap.get(maxLevelSku);
for (String skuLevel : skuLevels) {
Set<String> poNoTempSet = skuLevelMap.get(skuLevel);
poNoTempSet.addAll(chaJiSet);
// 递归计算
List<Set<String>> clist = doMergeClassDifferent(poNoTempSet, poMainInfoMap, calculatedSet);
if (CollectionUtils.isNotEmpty(clist)) {
resultList.addAll(clist);
}
}
}
}
return resultList;
}
2. 去重代码块
/**
* 去重 合单之后的采购单号
*
* @param sets
* @param dooModel
*/
private List<Set<String>> uniqueRepeatPoNo(List<Set<String>> sets, DooModel dooModel) {
sets.sort(new Comparator<Set<String>>() {
@Override
public int compare(Set<String> o1, Set<String> o2) {
return o2.size() - o1.size();
}
});
List<Set<String>> resultList = new ArrayList<>();
Set<String> allMergedSet = new HashSet<>();
Set<String> allSet = new HashSet<>();
for (Set<String> set : sets) {
Set<String> tempSet = new HashSet<>();
for (String poNo : set) {
if (!allSet.contains(poNo)) {
tempSet.add(poNo);
allMergedSet.add(poNo);
}
}
if (!tempSet.isEmpty()) {
if (tempSet.size() > 1) {
allSet.addAll(tempSet);
resultList.add(tempSet);
}
// 此处的单条后面不一定不能合单
}
}
// 差集
allMergedSet.removeAll(allSet);
if (allMergedSet.size() > 0) {
for (String poNo: allMergedSet) {
putPoNoToSet(dooModel, poNo);
}
}
return resultList;
}