Excel·VBA定量装箱、凑数值金额、组合求和问题
如图:对图中A-C列数据,根据C列数量按照一定的取值范围,组成一个分组装箱,要求如下:
1,每箱数量最好凑足50,否则为47-56之间;
2,图中每行数据不得拆分;
3,按顺序对分组装箱结果进行编号,如D列中BS0001;
4,生成分组装箱结果(包含B-C列数据),以及单独生成最终无法装箱的数据
目录
- 实现方法1
- 实现方法2
- 实现方法3
- 3种实现方法生成结果、对比、耗时
- 装箱结果整理
- 编号无序
- 编号有序
本问题本质上是组合求和问题,调用了combin_arr1函数,代码详见《Excel·VBA数组组合函数、组合求和》(如需使用代码需复制)
实现方法1
代码思路:持续不断组合
1,对数据读取为字典,行号为键数量为值;
2,对行号数组从2-N依次进行组合,判断是否符合取值范围;
3,对符合取值范围的行号组合,在res数组对应行号中写入装箱编号,并在字典中删除该行号
4,删除行号后,跳出后续循环遍历,并重复步骤2-3,直至无法删除行号,即没有符合范围的行号组合
5,在D列写入对应的装箱编号
注意:由于步骤4需要跳出循环,所以无法使用for…each遍历组合数组,否则报错该数组被固定或暂时锁定
Sub 装箱问题1()Dim arr, dict As Object, i&, j&, temp_sum, res, w&, dc&, brr, r&, c&target = 50: trr = Array(47, 56) '目标值,范围Set dict = CreateObject("scripting.dictionary"): tm = TimerWith Worksheets("数据") '读取数据arr = .[a1].CurrentRegion: ReDim res(1 To UBound(arr)): res(1) = "箱号"For i = 2 To UBound(arr)If arr(i, 3) = target Thenw = w + 1: res(i) = "BS" & Format(w, "000")Elsedict(i) = arr(i, 3)End IfNextdc = dict.CountDo '2层do方便有符合目标值时跳出,并继续组合DoFor j = 2 To dcbrr = combin_arr1(dict.keys, j)For r = 1 To UBound(brr)temp_sum = 0For c = 1 To UBound(brr(r))temp_sum = temp_sum + dict(brr(r)(c))NextIf temp_sum >= trr(0) And temp_sum <= trr(1) Thenw = w + 1For c = 1 To UBound(brr(r))res(brr(r)(c)) = "BS" & Format(w, "000"): dict.Remove brr(r)(c) '写入箱号,删除行号NextExit DoEnd IfNextNextIf dc = dict.Count Then Exit Do '无组合符合目标值,跳出Loop Until dc = 0If dc = dict.Count Then Exit Dodc = dict.CountLoop Until dc = 0.[d1].Resize(UBound(res), 1) = WorksheetFunction.Transpose(res)End WithDebug.Print "组合完成,累计用时" & Format(Timer - tm, "0.00") '耗时
End Sub
实现方法2
代码思路:遍历组合,跳过重复行号
与实现方法2类似,但步骤4不同,在字典删除行号后,继续遍历组合,并判断每个组合中是否存在被删除的行号,如果存在则跳过本组合,直至无法删除行号,或剩余行号无法支持下一轮递增元素个数进行组合
Sub 装箱问题2()Dim arr, dict As Object, i&, j&, temp_sum, res, w&, dc&, brr, r&, c&target = 50: trr = Array(47, 56) '目标值,范围Set dict = CreateObject("scripting.dictionary"): tm = TimerWith Worksheets("数据") '读取数据arr = .[a1].CurrentRegion: ReDim res(1 To UBound(arr)): res(1) = "箱号"For i = 2 To UBound(arr)If arr(i, 3) = target Thenw = w + 1: res(i) = "BS" & Format(w, "000")Elsedict(i) = arr(i, 3)End IfNextFor j = 2 To dict.CountIf j > dict.Count Then Exit For '所剩元素不足,结束brr = combin_arr1(dict.keys, j)For Each b In brrtemp_sum = 0For Each bb In bIf Not dict.Exists(bb) Thentemp_sum = 0: Exit For '重复跳过Elsetemp_sum = temp_sum + dict(bb)End IfNextIf temp_sum >= trr(0) And temp_sum <= trr(1) Thenw = w + 1For Each bb In bres(bb) = "BS" & Format(w, "000"): dict.Remove bb '写入箱号,删除行号NextEnd IfNextNext.[d1].Resize(UBound(res), 1) = WorksheetFunction.Transpose(res)End WithDebug.Print "组合完成,累计用时" & Format(Timer - tm, "0.00") '耗时
End Sub
实现方法3
实现方法1和实现方法2,都没有满足要求中“每箱数量最好凑足50”,仅对每行数量优先判断是否等于50,对于后续组合中都是符合范围即可
因此,对实现方法2添加1个for循环,第1遍组合满足target,第2遍组合满足目标值trr范围
Sub 装箱问题3()Dim arr, dict As Object, i&, j&, temp_sum, res, w&, dc&, brr, r&, c&target = 50: trr = Array(47, 56) '目标值,范围Set dict = CreateObject("scripting.dictionary"): tm = TimerWith Worksheets("数据") '读取数据arr = .[a1].CurrentRegion: ReDim res(1 To UBound(arr)): res(1) = "箱号"For i = 2 To UBound(arr)If arr(i, 3) = target Thenw = w + 1: res(i) = "BS" & Format(w, "000")Elsedict(i) = arr(i, 3)End IfNextFor n = 1 To 2 '第1遍组合满足target,第2遍组合满足目标值trr范围For j = 2 To dict.CountIf j > dict.Count Then Exit For '所剩元素不足,结束brr = combin_arr1(dict.keys, j)For Each b In brrtemp_sum = 0For Each bb In bIf Not dict.Exists(bb) Thentemp_sum = 0: Exit For '重复跳过Elsetemp_sum = temp_sum + dict(bb)End IfNextIf n = 1 And temp_sum = target Thenw = w + 1For Each bb In bres(bb) = "BS" & Format(w, "000"): dict.Remove bb '写入箱号,删除行号NextElseIf n = 2 And temp_sum >= trr(0) And temp_sum <= trr(1) Thenw = w + 1For Each bb In bres(bb) = "BS" & Format(w, "000"): dict.Remove bb '写入箱号,删除行号NextEnd IfNextNextNext.[d1].Resize(UBound(res), 1) = WorksheetFunction.Transpose(res)End WithDebug.Print "组合完成,累计用时" & Format(Timer - tm, "0.00") '耗时
End Sub
3种实现方法生成结果、对比、耗时
图中C列中的数量为1-50范围内的随机数,D列即为结果
分别对3种方法生成结果进行统计、对比:
方法1、2生成结果完全相同,数量分布不集中;方法3最终装箱的箱数也更少,且数量集中在50,但剩余行数多
400行数据测试,方法1、2剩余4行,方法3剩余15行
3种方法代码运行速度,分别测试300行、400行数据的耗时秒数
方法3对比方法2需要多生成、遍历一遍组合,由于组合数成指数递增,因此其400行相比300行耗时大幅增加,且电脑内存最高占用6G。如果要使用方法3且数据量较大,最好还是分段运行代码,避免耗时过久
装箱结果整理
编号无序
字典以箱号为键,值为数组
Sub 装箱结果输出1无序()Dim arr, dict As Object, i&, j&, r&, c&, max_c&, rng As Range, xh, dw, slSet dict = CreateObject("scripting.dictionary"): tm = TimerWith Worksheets("数据") '读取数据arr = .[a1].CurrentRegion: ReDim res(1 To UBound(arr) * 2, 1 To 10)res(1, 1) = "箱号": r = 0: Set rng = .Cells(1, 1).Resize(1, 3) '表头For i = 2 To UBound(arr)If Len(arr(i, 4)) Thenxh = arr(i, 4): dw = arr(i, 2): sl = arr(i, 3)If Not dict.Exists(xh) Thenr = r + 2: dict(xh) = Array(r, 2, sl) '箱号对应的行列号,数量合计res(dict(xh)(0), 1) = xh '箱号、单位号、数量赋值res(dict(xh)(0), dict(xh)(1)) = dwres(dict(xh)(0) + 1, dict(xh)(1)) = slElsec = dict(xh)(1) + 1: hj = dict(xh)(2) + sl '数量合计dict(xh) = Array(dict(xh)(0), c, hj)res(dict(xh)(0), dict(xh)(1)) = dw '单位号、数量赋值res(dict(xh)(0) + 1, dict(xh)(1)) = slmax_c = WorksheetFunction.Max(max_c, c) '最大列数End IfElseSet rng = Union(rng, .Cells(i, 1).Resize(1, 3))End IfNextEnd WithWith Worksheets("结果") '写入结果r = r + 1: max_c = max_c + 1: res(1, max_c) = "总件数"For i = 2 To rIf Len(res(i, 1)) = 0 Thenres(i, 1) = "数量": res(i, max_c) = dict(res(i - 1, 1))(2)End IfNextFor j = 2 To max_c - 1res(1, j) = "单位号" & (j - 1)Next.[a1].Resize(r, max_c) = resIf Not rng Is Nothing Then rng.Copy .Cells(1, max_c + 2) '无法装箱End WithDebug.Print "累计用时" & Format(Timer - tm, "0.00") '耗时
End Sub
生成结果:对方法2生成数据(即本文图1)进行整理
编号有序
字典嵌套字典,代码速度较无序版稍慢
为保证编号有序,以下代码使用了一维数组排序,调用了bubble_sort函数,代码详见《Excel·VBA数组冒泡排序函数》(如需使用代码需复制)
Sub 装箱结果输出2有序()Dim arr, dict As Object, i&, j&, r&, c&, max_c&, rng As Range, xh, dw, slSet dict = CreateObject("scripting.dictionary"): tm = TimerWith Worksheets("数据") '读取数据arr = .[a1].CurrentRegion: ReDim res(1 To UBound(arr) * 2, 1 To 10)res(1, 1) = "箱号": r = 0: Set rng = .Cells(1, 1).Resize(1, 3) '表头For i = 2 To UBound(arr)If Len(arr(i, 4)) Thenxh = arr(i, 4): dw = arr(i, 2): sl = arr(i, 3)If Not dict.Exists(xh) ThenSet dict(xh) = CreateObject("scripting.dictionary")End Ifdict(xh)(dw) = dict(xh)(dw) + slElseSet rng = Union(rng, .Cells(i, 1).Resize(1, 3))End IfNextkrr = bubble_sort(dict.keys) '有序箱号For Each k In krrr = r + 2: c = 1: res(r, c) = kFor Each kk In dict(k).keysc = c + 1: res(r, c) = kk: res(r + 1, c) = dict(k)(kk)Nextmax_c = WorksheetFunction.Max(max_c, c) '最大列数NextEnd WithWith Worksheets("结果") '写入结果r = r + 1: max_c = max_c + 1: res(1, max_c) = "总件数"For i = 2 To rIf Len(res(i, 1)) = 0 Thenres(i, 1) = "数量"res(i, max_c) = WorksheetFunction.sum(dict(res(i - 1, 1)).items)End IfNextFor j = 2 To max_c - 1res(1, j) = "单位号" & (j - 1)Next.[a1].Resize(r, max_c) = resIf Not rng Is Nothing Then rng.Copy .Cells(1, max_c + 2) '无法装箱End WithDebug.Print "累计用时" & Format(Timer - tm, "0.00") '耗时
End Sub
生成结果:对方法2生成数据(即本文图1)进行整理
附件:《Excel·VBA定量装箱、凑数值金额、组合求和问题(附件)》
扩展阅读:《excelhome-一个装箱难题》
相关文章:

Excel·VBA定量装箱、凑数值金额、组合求和问题
如图:对图中A-C列数据,根据C列数量按照一定的取值范围,组成一个分组装箱,要求如下: 1,每箱数量最好凑足50,否则为47-56之间; 2,图中每行数据不得拆分; 3&…...

通过Jmeter压测存储过程
目录 一、存储过程准备: 二、测试工具准备: 三、工具配置及执行: 1、配置JDBC Connection Configuration: 2、配置吞吐量控制器(可跳过): 3、配置JDBC Request: 对于存储过程…...

Spring笔记之Spring对IoC的实现
文章目录 IoC控制反转依赖注入set注入注入外部Bean注入内部Bean注入简单类型通过注入方式实现javax.sql.DateSource接口测试简单类型 级联属性赋值(了解)注入数组注入List集合注入Set集合注入Map集合注入Properties注入null和空字符串不给属性赋值使用 注…...

【eNSP】Telnet远程登录
Telnet远程登录 eNSP软件TelnetTelnet远程登录-路由连接关闭防火墙eNSP根据图1画图路线配置路由端口IP配置路由R1改名配置接口IP 配置路由R2 配置R2的远程登录设置登录用户授权级别退出登录超时时间 Telnet测试 eNSP软件 eNSP(Enterprise Network Simulation Platform)是一款由…...

SOP/详解*和**/python数据结构(iter,list,tuple,dict)/ 解包
一、错误解决合集 1. > combined_seq.named_children() 2. isinstance 2th parameter : must be a type or tuple of types > 改为tuple,不要用列表。改为 LLLayer (nn.Conv2d,nn.Linear) 3. File “test.py”, line 90, in calculate_fin_fout print(“hi”…...

使用webdriver-manager解决浏览器与驱动不匹配所带来自动化无法执行的问题
1、前言 在我们使用 Selenium 进行 UI 自动化测试时,常常会因为浏览器驱动与浏览器版本不匹配,而导致自动化测试无法执行,需要手动去下载对应的驱动版本,并替换原有的驱动,可能还会遇到跨操作系统进行测试的时候&…...

【vue】Vue中debugger报错 unexpected ‘debugger’ statement no-debugger
前言: Vue中debugger报错 unexpected ‘debugger’ statement no-debugger (意外的“调试器”语句没有调试器) eslink规则没有开启’debugger’ ,被规则屏蔽了,需要手动放开 解决方法 方式一: 找到.esl…...

课题方向a
首先在无线感知的研究方向下,辅以深度学习和计算机视觉的技术和知识,可以从事哪些具体课题的研究?请你尽可能多的给出课题名称供我选择 在无线感知的研究方向下,辅以深度学习和计算机视觉的技术,有很多具体课题可以进行研究。以下是一些供您选择的课题名称: 基于深度学习…...

【Matter】基于Ubuntu 22.04 交叉编译chip-tool
编译工程之际,记录一下编译过程,免得后续遗忘,总结下来chip-tool 交叉编译涉及到的知识点: 需要了解如何支持交叉编译,基于GN编译框架需要理解应用库如何交叉编译,理解pkg-config的使用meson 编译…...

Qt/C++音视频开发50-不同ffmpeg版本之间的差异处理
一、前言 ffmpeg的版本众多,从2010年开始计算的项目的话,基本上还在使用的有ffmpeg2/3/4/5/6,最近几年版本彪的比较厉害,直接4/5/6,大版本之间接口有一些变化,特别是一些废弃接口被彻底删除了,…...

低碳 Web 实践指南
现状和问题 2023年7月6日,世界迎来有记录以来最热的一天。气候变化是如今人类面临的最大健康威胁。据世界卫生组织预测2030年至2050年期间,气候变化预计每年将造成约25万人死亡。这是人们可以真切感受到的变化,而背后的主要推手是碳排放。 …...

信息安全:网络安全体系 与 网络安全模型.
信息安全:网络安全体系 与 网络安全模型. 网络安全保障是一项复杂的系统工程,是安全策略、多种技术、管理方法和人员安全素质的综合。一般而言,网络安全体系是网络安全保障系统的最高层概念抽象,是由各种网络安全单元按照一定的规…...

【云原生】Serverless 技术架构分析
一、什么是Serverless? 1、Serverless技术简介 Serverless(无服务器架构)指的是由开发者实现的服务端逻辑运行在无状态的计算容器中,它由事件触发, 完全被第三方管理,其业务层面的状态则被开发者使用的数据库和存…...

Visual Studio Code 设置文件头部添加作者、日期和函数注释
step1:安装插件KoroFileHeader step2:左下角选择管理—设置—输入"fileheader"—点击"在setting.json中编辑" step3:添加下面的代码到json文件中 // 文件头部注释 "fileheader.customMade": {"Descripttion":"","ve…...

HCIA云计算 V5.0题库
云计算,这是近几年听得最多词了,云计算对于网络的发展帮助非常大,它自身所产生的价值是不可估量的!所以云计算的岗位对于很多IT公司来说,都是有一定地位的。华为认证云计算面向的对象很简单就是对云计算技术感兴趣的人…...

基于Matlab实现帧间差分法的运动目标检测(附上完整源码+图像+程序运行说明)
帧间差分法是一种常用的运动目标检测方法,可以通过对连续帧之间的差异进行分析来确定目标的运动情况。在本文中,我们将介绍如何使用Matlab实现帧间差分法的运动目标检测。 文章目录 部分源码完整源码图像程序运行说明下载 部分源码 首先,我们…...

Jenkins搭建最简教程
纠结了一小会儿,到底要不要写这个,最终还是决定简单记录一下,因为Jenkins搭建实在是太简单了,虽然也有坑,但是坑主要在找稳定的版本上。 先学一个简称,LTS (Long Term Support) 属实是长见识了,…...

设置git可以同时推送gitee和github
查看当前的远程仓库设置: git remote -v 这会列出你当前配置的远程仓库。你可能会看到类似以下的输出:origin-gitee <gitee仓库地址> (fetch)origin-gitee <gitee仓库地址> (push) 新增一个远程仓库 git remote add origin-github <githu…...

Java给Excel设置单元格格式
maven 依赖 <!--读取excel文件--> <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version> </dependency> <dependency><groupId>org.apache.poi</group…...

__block的深入研究
__block可以用于解决block内部无法修改auto变量值的问题 __block不能修饰全局变量、静态变量(static) 编译器会将__block变量包装成一个对象 调用的是,从__Block_byref_a_0的指针找到 a所在的内存,然后修改值 第一层拷贝&…...

Segment anything(图片分割大模型)
目录 1.Segment anything 2.补充图像分割和目标检测的区别 1.Segment anything 定义:图像分割通用大模型 延深:可以预计视觉检测大模型,也快了。 进一步理解:传统图像分割对于下图处理时,识别房子的是识别房子的模型…...

【雕爷学编程】MicroPython动手做(27)——物联网之掌控板小程序3
知识点:什么是掌控板? 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片,支持WiFi和蓝牙双模通信,可作为物联网节点,实现物联网应用。同时掌控板上集成了OLED…...

Java中集合容器详解:简单使用与案例分析
目录 一、概览 1.1 Collection 1. Set 2. List 3. Queue 1.2 Map 二、容器中的设计模式 迭代器模式 适配器模式 三、源码分析 ArrayList 1. 概览 2. 扩容 3. 删除元素 4. 序列化 5. Fail-Fast Vector 1. 同步 2. 扩容 3. 与 ArrayList 的比较 4. 替代方案…...

机器学习04-数据理解之数据可视化-(基于Pima数据集)
什么是数据可视化? 数据可视化是指通过图表、图形、地图等视觉元素将数据呈现出来的过程。它是将抽象的、复杂的数据转化为直观、易于理解的视觉表达的一种方法。数据可视化的目的是帮助人们更好地理解数据,从中发现模式、趋势、关联和异常,从而作出更明…...

百度@全球开发者,见证中国科技超级“碗”!
潮汐涌动时,变化悄然发生。2023年全球AI浪潮迭起,大语言模型热度空前,生成式人工智能为千行百业高质量发展带来更多想象空间,一个蓬勃创新、重构万物的“大模型时代”正蓄势待发。 滴滴滴~百度全球开发者,…...

分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离(一)
说明:请先自行安装好docker再来看本篇文章,本篇文章主要实现通过使用docker部署mysql实现读写分离,并连接数据库测试。第二篇将实现使用Shardingjdbc实现springboot的读写分离实现。 基于Docker去创建Mysql的主从架构 #创建主从数据库文件夹…...

Ajax跨域问题
什么是跨域问题? 跨域问题来源于JavaScript的"同源策略",即只有 协议主机名端口号 (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的&…...

Vue + FormData + axios实现图片上传功能
当使用Vue FormData axios实现图片上传功能时,你可以按照以下步骤进行操作: 示例代码 首先,在Vue组件中,创建一个data属性来存储选择的文件和上传状态: data() {return {file: null,uploading: false}; }在模板中…...

设计模式系列:经典的单例模式
单例模式,是设计模式当中非常重要的一种,在面试中也常常被考察到。 正文如下: 一、什么时候使用单例模式? 单例模式可谓是23种设计模式中最简单、最常见的设计模式了,它可以保证一个类只有一个实例。我们平时网购时用的购物车,就是单例模式的一个例子。想一想,如果购物…...

macbook pro 散热解决办法
结论: 2017 macbook pro 13.3 寸 控制住温度, 不惧长时间满载、性能也飞起. 方案说明最低温度满载温度一、终极方案(成本 460元)120w半导体散热 导热垫31度71度二、推荐方案, 完全静音(成本 50元)828散热风扇 导热垫43度81度三、不拆机、低成本(20元)828散热风扇56度91度四、…...