递归算法实践--到仓合单助力京东物流提效增收

news/2025/9/28 18:57:04/文章来源:https://www.cnblogs.com/Jcloud/p/19117405

一、背景

京东物流到仓业务「对商家」为了减少商家按照京东采购单分货备货过程,对齐行业直接按照流向交接,提升商家满意度;「对京东」揽收操作APP提效;到仓合单功能应运而生;

二、问题

一次批量采购单(一次50或者100个采购单)需要根据不同的规则合并成多个订单;

每一个采购单可以是不同的来源类型(自营和非自营)、不同的收货类型,每一个采购单会有多个SKU,同一个SKU只有一个等级,一批采购单会有多个SKU,同一个SKU会有多个等级;

合单规则:

1.自营和非自营不能合;

2.实物收货和单据收货的采购单不能合并;

3.相同收获仓和配送中心的采购单可以合并;

4.两个采购单如果合并之后同一个SKU拥有多个等级,则不可以合单;

三、打法

A、思路

1.首先认为这一批单子可以合单,后续就是根据合单规则将不符合规则转换成拆单的过程;

2.根据合单规则1、2、3可以将这一批单子拆成多个需要执行规则4的待合单集合List;

3.举个极端例子,规则1、2、3这些采购单都是相同的,则该List数量为1,这100个单子进行后续根据SKU+等级维度的合单;

4.由于相同SKU不同等级不可以合单,我们可以先找出这100个采购单中包含最多等级的SKU,比如skuA 包含最多的7个等级, 根据skuA进行按等级进行分堆,分成7堆之后,由于并不是所有的采购单都包含skuA, 则这100个采购单可能还会剩下一些单子不在这7堆之内,也就是剩下的这些单子如果只是基于skuA维度进行分堆,可以跟这7堆任何一堆进行合单,这时候需要将这些剩下的单子分别加入到这7堆里面,得到第一次合单后的结果,这里很重要,也是纳入递归算法的基础;

5.得到的7堆再分别进行第四步的操作,直到当前这一堆的sku不包含不同等级为止(这里是递归结束的条件);

6.由于分堆里面包含了重复的订单,所以有些单子组合会被重复计算,这时候需要维护一个列表将计算过的单据进行保存,这样可以将重复的列表进行剪枝,这样可以保证整个算法的时间复杂度不是指数级增长;

7.针对最终全部递归之后的结果将合单的列表进行由多到少进行排序,然后进行排重,这里如果排重之后只有一个采购单了可以先释放,但不要加到排重列表里面,因为后面可能还会出现可合并的集合,很重要,不然得到的合单结果会变少,得到最终的合单后的结果;

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, 值为poNosMap<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, 值为poNosMap<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;// 最多等级个数的对应skuString 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下的poNosSet<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>>() {@Overridepublic 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/921005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开发手机网站用什么语言做电商设计有什么好的网站推荐

目录 k8s是什么 k8s不是什么 云原生 微服务 整体式架构与微服务架构 微服务的特性 微服务的优势 k8s是什么 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快…

网站空间150m分级会员管理系统网站开发

说来惭愧&#xff0c;从事互联网开发好些年了&#xff0c;有些概念一直没有彻底搞清楚。其中之一就是,ttp client配置连接池的时候,maxConnectionsPerHost,和maxTotalConnections,在网上搜了一圈&#xff0c;发现很多都讲的含含糊糊的。 作者&#xff1a;资深开发讲技术 背景 …

计算机视觉(opencv)练习——抠图(图像裁剪与轮廓提取) - 详解

计算机视觉(opencv)练习——抠图(图像裁剪与轮廓提取) - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

完整教程:上下文工程驱动智能体向 透明化推理日志

完整教程:上下文工程驱动智能体向 透明化推理日志pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

基于Mysql+SpringBoot+vue框架-在线宠物用品交易网站的设计与实现 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:@scqilin/phone-ui 手机外观组件库

深入解析:@scqilin/phone-ui 手机外观组件库pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &qu…

北京网站设计权威乐云践新站建设 app开发网站

原 Linux搭建SVN 服务器 发表于1年前(2014-08-05 17:55) 阅读&#xff08;12257&#xff09; | 评论&#xff08;3&#xff09; 31人收藏此文章, 我要收藏赞3摘要 Linux搭建SVN 服务器目录[-] Linux搭建SVN 服务器1 安装SVN2 使用客户端连接2.1 使用…

全景网站如何建设虚拟主机登录wordpress管理后台

本篇文章包含内容较多&#xff0c;请参照目录浏览,在每一部分结束或,有该部分可能遇到的问题0、开始本教程前请先备份电脑中的重要文件&#xff01;&#xff01;&#xff01;&#xff01;一、Ubuntu20.04 搭建更详细的内容请参照该博客&#xff1a;https://www.cnblogs.com/mas…

Tita项目与绩效一体化管理:驱动企业效能跃升的数字化引擎

在数字经济时代,企业竞争的核心已从单一业务能力转向组织整体效能的协同释放。项目管理作为企业战略落地的关键抓手,正面临从”任务执行”到”价值创造”的范式转变。Tita项目经营管理一体化平台,凭借其独特的项目与…

第七篇

今天是9月28日,今天是星期日但是补星期三的课,调休这玩意真是烦,早上上的离散和马克思主义基本原理,离散没咋明白,马原倒是听进去不少。

网站建设制作方法怎样建自己的网站免费的

1.IDEA修改maven路径无效 创建spring项目&#xff0c;Maven导入报错&#xff0c;无法正常导入jar报&#xff0c;发现setting中设置的maven路径不是自己下载的路径&#xff0c;修改后无效。运行之后maven路径又恢复为其默认的路径 解决方案&#xff1a; 删除.mvn文件&#xff0…

升级openssl影响

升级openssl影响在 CentOS(或者 RHEL 系)里,要判断 哪些软件依赖 OpenSSL,可以用几个不同层级的办法:1️⃣ 查询已安装软件包依赖(RPM 层面) # 哪些包依赖 openssl-libs rpm -q --whatrequires openssl-libs# 或…

Day23抽象类

package oop1.Demo8;//抽象类中,所由继承了它的子类,必须要实现它的方法~除非子类也是抽象类,那就继续套娃 public class A extends Action{@Overridepublic void dosomething() {} }package oop1.Demo8;//抽象类 …

陕西正天建设有限公司网站可以自己做网站赚钱吗

在宝塔面板环境下安装nps服务端 一、所需环境二、开始安装三、打开nps控制台四、更改默认账号密码和连接秘钥五、反向代理挂载SSL证书 一、所需环境 阿里云轻应用服务器&#xff08;选择宝塔应用镜像&#xff09;域名&#xff08;最好也是阿里注册的域名&#xff09;对应的ssl…

做影视网站犯法吗不用wordpress 知乎

总结&#xff1a; 初级&#xff1a;简单修改一下超参数&#xff0c;效果一般般但是够用&#xff0c;有时候甚至直接不够用 中级&#xff1a;optuna得出最好的超参数之后&#xff0c;再多一些epoch让train和testloss整体下降&#xff0c;然后结果就很不错。 高级&#xff1a;…

完整教程:开源的 CSS 动画库

完整教程:开源的 CSS 动画库pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco"…

ES 是否有类似mysql explain的语句诊断用法

目录一、explain API:分析查询匹配与索引使用1. 基本用法方式1:查询时添加 explain=true方式2:针对单个文档分析(推荐)2. 核心返回结果解读二、profile API:分析查询执行性能1. 基本用法2. 核心返回结果解读三、…

让每次语音唤醒都可靠,公牛沐光重构可观测体系

本文详细介绍了公牛沐光团队如何从开源监控方案 SkyWalking 成功迁移到阿里云 ARMS,构建起一套集观测、追踪、日志分析与智能告警于一体的全栈式可观测平台的实践历程。作者:公牛集团,周施乐 王雷 欧阳绩新;阿里云…

【2025-09-27】连岳摘抄

23:59自信与骄傲有异:自信者常沉着,而骄傲者常浮扬。——梁启超高三可能是人生压力最大的一年。高三生身体上发育成熟了,但大脑还没有,不能很好地控制自己的情绪。压力巨大与大脑未成熟,这对矛盾造成高三生容易暴…