ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 29009|回复: 77

[分享] 利用背包算法解决分组凑数问题

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2017-7-24 22:05 | 显示全部楼层 |阅读模式
分组凑数问题,最近几年问的人也比较多。

问题描述如下:

【分组凑数问题】
从已知数据中,寻找<=目标值的组合,勾选列出后继续,直至找不到符合条件的组合为止。

【限制条件】:
每一组合中的数据不可重复使用。
尽量提取最接近目标值的组合。

……
通常,我使用随机算法、或使用香川凑数算法的Do循环解决问题。速度效率令人满意。
但是代码算法不容易被人理解,所以我也一直没有分享给大家。

……
最近在研究背包算法时,想到这个也可以用作凑数,进一步用于分组凑数,
今天就把背包算法代码进行了分组凑数的适用化处理,写成了可以随意使用的程序,分享给大家。

动态规划背包算法解决分组凑数问题.zip

17.96 KB, 下载次数: 1539

点评

香川对算法的执着,足够成为大师了  发表于 2017-7-29 16:51

评分

20

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-7-24 23:04 | 显示全部楼层
似乎也可以用来解决一维下料问题……

但是目前是手工确定选用合适规格,应该要能自动化才算实用。

动态规划背包算法解决分组凑数问题.zip

26.77 KB, 下载次数: 727

评分

7

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2017-7-24 23:05 | 显示全部楼层
代码以及注释:

  1. Sub test() 'by kagawa 2017/7/24 背包算法解决分组凑数问题
  2.     Dim ar, br, cr(), h&, k&, l&, m&, n&, r&, tms#
  3.     tms = Timer
  4.    
  5.     m = [b2].End(4).Row - 1 '数据个数m
  6.     ar = [b2].Resize(m, 3) '读取B:D 3列 分组原始数据、权重、备注信息
  7.     br = [g2].Resize(m) '读取G列 分组目标限额数据
  8.     ReDim cr(1 To m) '定义记录分组序号的数组cr
  9.    
  10.     [g1].CurrentRegion.Offset(1, 1) = ""
  11.     r = m
  12.     If WorksheetFunction.Count([c2].Resize(m)) = m Then l = 2 Else l = 1 '检查C列是否为权重数据
  13.     For k = 1 To m '依次进行分组计算
  14.         
  15.         h = br(k, 1): If h = 0 Then h = br(k - 1, 1): br(k, 1) = h: Cells(k + 1, 7) = h
  16.         '读取分组目标限额、如本行为空则继承上一行数值h
  17.         
  18.         n = GetDP(ar, cr, h, k, l) '调用背包算法 进行<=h的凑数 并返回有效组的元素个数
  19.         
  20.         If n = 0 Then Exit For '如找不到解则退出
  21.         
  22.         r = r - n '计算数据剩余个数r
  23.         If r = 0 Then Exit For '如r=0即全部数据都已经分组完毕也退出
  24.         
  25. '        [e2].Resize(m) = WorksheetFunction.Transpose(cr)'调试时输出每一步的分组结果
  26.     Next
  27.    
  28.     [e2].Resize(m) = WorksheetFunction.Transpose(cr) '输出最终分组记录结果
  29.     [a2].Resize(m, 5).Sort [e2], 1, [b2], , 1, [a2], 1, 2 '分组结果排序
  30.     MsgBox Format(Timer - tms, "0.000s") & vbCr & "已经完成 " & k & " 个分组"
  31. End Sub
  32. Function GetDP(ar, cr, h&, k&, l&) '老窖提供生成DP_Knapsack表的算法、香川2017/07/23 修改
  33.     Dim sj&(), DP&(), i&, j&, m&, n&, v&, v1&, r&, s1$, s2$
  34.    
  35.     m = 0: ReDim sj&(1 To UBound(ar), 2)
  36.     For i = 1 To UBound(ar)
  37.         If cr(i) = 0 Then m = m + 1: sj(m, 0) = i: sj(m, 1) = ar(i, 1): sj(m, 2) = ar(i, l)
  38.     Next
  39.     '以上对照扣除已分组数据,把剩余有效数据重新整理、写入sj数组
  40.    
  41.     ReDim DP&(m, h) '定义DP表数组
  42.     For i = 1 To m '填写DP表
  43.         For j = 1 To h
  44.             If sj(i, 1) > j Then
  45.                 DP(i, j) = DP(i - 1, j) '加入后超过当前值则不可加入,直接继承上一行结果
  46.             Else
  47.                 v1 = DP(i - 1, j - sj(i, 1)) + sj(i, l) '计算加入背包后的结果v1
  48.                 v = DP(i - 1, j) '不加入背包时的结果v
  49.                 If v1 > v Then DP(i, j) = v1 Else DP(i, j) = v '取上述两种结果中的最大值
  50.             End If
  51.         Next
  52.     Next
  53.    
  54.     '按上述动态规划的背包算法遍历所有可能的组合得到最大值的DP数据表,据此可以得到所有解
  55.     i = m: j = h '下面代码只选取第1种低位有效组合
  56.     Do
  57.         Do
  58.             If DP(i, j) = DP(i, j - 1) Then j = j - 1 Else Exit Do '背包值相同时左移1列
  59.         Loop Until j = 0
  60.         Do
  61.             If DP(i, j) = DP(i - 1, j) Then i = i - 1 Else Exit Do '背包值相同时上移1行
  62.         Loop Until i = 1
  63.         If DP(i, j) = 0 Then Exit Do '无解时退出
  64.         
  65.         '背包值不同时即可提取本组的对应元素
  66.         n = n + 1 '记录本组使用元素个数
  67.         cr(sj(i, 0)) = k '数组cr中记录分组序号k
  68.         r = r + sj(i, 1) '统计本组凑数总和r
  69.         s1 = s1 & "+" & sj(i, 1) '合并本组凑数结果字符串
  70.         s2 = s2 & "+" & ar(sj(i, 0), 3) '合并本组信息字符串
  71.         j = j - sj(i, 1): i = i - 1 '
  72.     Loop Until DP(i, j) = 0 '提取完毕退出
  73.    
  74.     If n Then '本组有效时 输出结果到工作表
  75.         Cells(k + 1, 8) = "=" & Mid(s1, 2) '本组凑数总和
  76.         Cells(k + 1, 9) = h - r            '本组和目标值的差额
  77.         Cells(k + 1, 10) = Mid(s2, 2)      '本组信息合并字符串
  78.     End If
  79.    
  80.     GetDP = n '函数返回本组含元素个数n
  81. End Function
复制代码

评分

9

查看全部评分

TA的精华主题

TA的得分主题

发表于 2017-7-25 06:54 来自手机 | 显示全部楼层
占位学习。收藏备用。向高手致敬

TA的精华主题

TA的得分主题

发表于 2017-7-25 11:27 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2017-7-25 11:44 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
这个好----

TA的精华主题

TA的得分主题

发表于 2017-7-25 16:23 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
站位学习。

TA的精华主题

TA的得分主题

发表于 2017-7-25 16:40 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2017-7-25 17:35 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2017-7-25 20:05 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-12-23 17:12 , Processed in 0.053459 second(s), 11 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表