ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: aoe1981

[原创] 自力更生:钢筋下料组合优化正式版发布(一维)

  [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-28 16:15 | 显示全部楼层
  对比测试二:
  将数据改大:
序号
下料长度
数量/根
1
1200
9
2
2650
5
3
6350
7
4
6000
3
5
3000
8


则法师的附件为优!

图如下:
对比1.jpg
对比2.jpg
看来,我要努力实现随机算法了……

我的“排序+小遍历”看来是凑巧的……

惭愧惭愧!!!

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-28 16:23 | 显示全部楼层
本帖最后由 aoe1981 于 2014-10-31 22:24 编辑

  如果实现了随机算法,我想就可以摆脱对原始数据排序的依赖了……

  我采用排序的策略,可能是基于:优先搭配大料,其次是小料的思路影响……这种思路很狭隘!!!

  所谓“小遍历”,即是实现了每种规格的料都有可能进行优先搭配……这个和上面的思路似乎有所不协调,可能更接近随机的方法,但不是真正地随机,可能我是受到了偶然数据表现出的最优结果的误导了……

  其实,我尝试过可控大量的随机算法,我的做法是用[香川经典数组洗牌法]把原始数据打乱,重新搭配,然后沉淀优秀方案……
  但我在测试时发现,还不如我上面的做法,那怕随机到很大的次数上……

  我想,我没有发现随机算法的真谛……

  现在想来,随机算法应当有以下特点:
  必须要使整个随机过程呈现收敛的特点,要向最优方案逼近……这个过程不能是随机不可控的、漫无目标的,更不能是发散的……

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-29 15:29 | 显示全部楼层
  经测试研究发现,5楼所罗列的两条优化标准的第2条:
  “余料方差最大”
  不太妥帖。


  这条标准目前不如改换为:
  余料根数最少。
  因为,有种极端情况下,余料方差也会很大,比如:实现的余料长度为4000,假设这4000的余料如此分布:
  3500,100,100,100,100,100  (此时方差为:12300000)
  这时得到的余料方差就很大,但是余料根数很多,往往不如下面的方案:
  1000,1000,1000,1000      (此时方差为:4000000)


  所以,优化的标准要有次序:
  1.余料要少;
  2.余料根数要少;
  3.余料方差要大。


  下面传一个不太成熟的随机版:
   钢筋下料优化(随机版).rar (50.5 KB, 下载次数: 239)

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-29 15:33 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
  上楼附件采用了上楼1、2两条标准,名为随机,不太随机,待进一步研究……
  在许多与法师附件的对比测试中发现,还是不如法师附件中的算法。因为我的算法其实是伪随机……

  现在,也有了随机的思路,需重写代码了……能利用的代码估计得排除之前的所谓“核心代码”了……希望能成功,是一个值得一试的思路……

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-30 08:30 | 显示全部楼层
本帖最后由 aoe1981 于 2014-10-30 08:36 编辑

  实现了真正随机算法的附件已发布,见1楼,先占楼,再做介绍。
  所以叫正式版,有两大原因:
  一、实现了随机优化的算法;
  二、实现了14楼关于“优化”的三条标准:
  1.余料要少;
  2.余料根数要少;
  3.余料方差要大。

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-30 08:40 | 显示全部楼层
  正式版附件一角:
   258.jpg

  该附件一直在与法师“一维下料”附件的对比测试中进行不断完善,下面再传两个相关附件:
  尊敬的法师的附件:
   一维下料.rar (31.17 KB, 下载次数: 461)
  我的方差验证附件:
   方差验算.rar (5.63 KB, 下载次数: 246)

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-30 08:51 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 aoe1981 于 2014-11-1 21:26 编辑

  是怎样的随机算法呢?
  我们想像一下:
  假设下料的总根数为n,首先要实现【起点随机】,这一步是通过【香川经典数组洗牌法】打乱原始数据实现的,为何不直接随机抽取一个作为随机抽取的起点呢?是因为这样做,始终使随机抽取的第一根料保持在首位,从而使得下料搭配只考虑“向后搭配”,便于代码的展开。其实我前面所发的一个“伪随机”附件已经做到了这一点,但光这一点远远不够,还需要【搭配过程随机】相辅助,才能真正模拟随机的状态,这也正是前面附件所欠缺的,也正是“正式版”的核心优点!




  随机抽取第1根料后,假设抽取的是n1,可与之进行搭配的料设有m1个,构成一个集合,这个m1大多数时候应当比n-1要小。好了,此时,在这个集合中,不再按序搭配了,而是改成随机抽取搭配,假设抽取到的是n2。


  下面再看当前的搭配:n1+n2,如果可以继续搭配的话,可与之搭配的集合设为m2,这时的元素个数多数时候应当比m1要小。好了,继续从中随机抽取搭配。


  ……


  好的,接下来继续重复上述过程。

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-30 08:56 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 aoe1981 于 2014-11-1 21:29 编辑

  附件中全部代码如下:

  1. Option Explicit
  2. Public Sub YouHua() '优化
  3. '准备工作与转存数组
  4. Range("f12:f16,f21:f24").ClearContents
  5. Range("i2:i" & Rows.Count).ClearContents
  6. Range("k2:v" & Rows.Count).ClearContents
  7. Dim m%, n&, arr, i&, j&, k&, lzh#, lduan#, js&, gd#
  8. lzh = Range("f1").Value '整料
  9. lduan = Range("f3").Value '短料
  10. m = Range("f4").Value '种数
  11. n = Range("f5").Value '根数
  12. If m = 0 Then MsgBox "请输入下料长度与数量!", , "友情提示": Exit Sub
  13. ReDim yssj#(1 To n), crr#(1 To m, 1 To 2) '原始数据
  14. arr = Range("b2:c31").Value
  15. k = 0 '计数初始化
  16. For i = 1 To 30 '排除arr中空值,形成crr数组
  17.     If arr(i, 1) <> "" And arr(i, 2) <> "" Then
  18.         k = k + 1
  19.         crr(k, 1) = arr(i, 1): crr(k, 2) = arr(i, 2)
  20.     End If
  21. Next i
  22. k = 0
  23. For i = 1 To m '转存一维数组
  24.     For j = 1 To crr(i, 2)
  25.         k = k + 1
  26.         yssj(k) = crr(i, 1)
  27.     Next j
  28. Next i



  29. '排除整料与准整料(即余料小于短料)
  30. ReDim jg11(1 To 1, 1 To m), jg12(1 To 1, 1 To m) '结果1
  31. js = 0 '输出第一部分结果时的行数
  32. For i = 1 To m
  33.     If crr(i, 1) > lzh - lduan Then
  34.         js = js + 1
  35.         jg11(1, js) = crr(i, 1)
  36.         jg12(1, js) = crr(i, 2)
  37.     End If
  38. Next i
  39. If js > 0 Then
  40.     ReDim Preserve jg11(1 To 1, 1 To js), jg12(1 To 1, 1 To js)
  41.     Range("i2").Resize(js) = WorksheetFunction.Transpose(jg12) '输出根数
  42.     Range("k2").Resize(js) = WorksheetFunction.Transpose(jg11) '输出规格
  43. End If



  44. '整理原始数据
  45. Dim n1& '需搭配的下料根数
  46. n1 = n - WorksheetFunction.Sum(jg12)
  47. If n1 = 0 Then MsgBox "下料长度不能搭配,不需要优化!", , "友情提示": Exit Sub
  48. ReDim sj#(1 To n1), sj1#(1 To n1), sj2$(1 To n1), jg$(1 To n1)
  49. k = 0
  50. For i = 1 To n
  51.     If yssj(i) <= lzh - lduan Then
  52.         k = k + 1
  53.         sj(k) = yssj(i) '排除整料和准整料后的原始数据
  54.         sj1(k) = yssj(i) '用于搭配求和
  55.     End If
  56. Next i
  57. Range("f12") = WorksheetFunction.Max(sj) '搭配料最长
  58. Range("f13") = WorksheetFunction.Min(sj) '搭配料最短
  59. Range("f14") = m - js '搭配料种数
  60. Range("f15") = n1 '搭配料根数
  61. Range("f16") = WorksheetFunction.Sum(sj) '搭配料总长
  62. If n1 = n Then
  63.     MsgBox "没有整料和准整料!" & Chr(10) & Chr(10) & "下面即将开始可以搭配下料的组合优化,请耐心等待!", , "友情提示"
  64. Else
  65.     MsgBox "不能搭配下料的整料和准整料已排除完毕!" & Chr(10) & Chr(10) & "下面即将开始可以搭配下料的组合优化,请耐心等待!", , "友情提示"
  66. End If



  67. '需搭配的下料组合优化(★★★核心代码★★★)
  68. Dim t#, pd As Boolean, yl#, minyl#, r&, h%, l%, brr, sxgs&
  69. Dim ylgs&, minylgs&, ylfc#, ylfc1#, maxylfc#, sjcs&, ii&, i3&, sjxb&, dapaizhi# '随机次数、随机下标、搭配值
  70. t = Timer
  71. minyl = Range("f16").Value '最小余料
  72. sxgs = 0 '实现根数
  73. minylgs = n1 '最小余料根数
  74. maxylfc = 0 '最大余料方差
  75. sjcs = Range("f27").Value * 1000
  76. Randomize
  77. For ii = 1 To sjcs
  78.     For k = 1 To n1 - 1
  79.         If sj1(k) > 0 Then '跳过已搭配的料
  80.             dapaizhi = sj1(k) '当前搭配值
  81.             sj2(k) = sj1(k) '搭配组合连接初值
  82. 100:
  83.             ReDim sj3#(1 To n1)
  84.             i3 = 0
  85.             For i = k + 1 To n1
  86.                 If sj1(i) > 0 And sj1(i) <= lzh - dapaizhi Then '筛选出当前dapaizhi的可搭配料存入sj3,sj3的元素可变
  87.                     i3 = i3 + 1
  88.                     sj3(i3) = sj1(i)
  89.                 End If
  90.             Next i
  91.             If i3 > 0 Then '存在可搭配料时
  92.                 ReDim Preserve sj3#(1 To i3)
  93.                 sjxb = Int(Rnd() * i3) + 1 '★随机搭配可搭配的下料规格★
  94.                 dapaizhi = dapaizhi + sj3(sjxb)
  95.                 sj1(k) = dapaizhi
  96.                 sj2(k) = sj2(k) & "+" & sj3(sjxb)
  97.                 For i = k + 1 To n1
  98.                     If sj1(i) = sj3(sjxb) Then sj1(i) = 0: Exit For '已搭配料归0
  99.                 Next i
  100.                 GoTo 100
  101.             End If
  102.         End If
  103.     Next k
  104.     If sj1(n1) > 0 Then sj2(n1) = sj1(n1) '防止最后一个值未被搭配时遗漏
  105.     yl = 0: ylgs = 0: ylfc = 0
  106.     For i = 1 To n1
  107.         If sj1(i) <> 0 Then
  108.             yl = yl + (lzh - sj1(i)) '计算余料
  109.             If sj1(i) < lzh Then ylgs = ylgs + 1 '计算余料根数(搭配后小于整料长度)
  110.             ylfc = ylfc + (lzh - sj1(i)) ^ 2 '计算余料方差
  111.         End If
  112.     Next i
  113.     If yl <= minyl And ylgs <= minylgs Then '记录相对较优方案:余料少、余料根数少
  114.         If ylfc > maxylfc Then '余料方差大
  115.             For i = 1 To n1 '结果出口1
  116.                 jg(i) = sj2(i)
  117.             Next i
  118.             ylfc1 = ylfc '用于正确输出结果
  119.             pd = True
  120.         End If
  121.         If ylgs < minylgs Then
  122.             maxylfc = ylfc '★余料根数减少时重新记录最大余料方差★
  123.             For i = 1 To n1 '结果出口2
  124.                 jg(i) = sj2(i)
  125.             Next i
  126.             pd = False
  127.         End If
  128.         minyl = yl: minylgs = ylgs
  129.     End If
  130.     Randomize
  131.     For i = 1 To n1 '利用【香川经典数组洗牌法】乱序恢复sj1数组数据,准备下一次随机搭配
  132.         r = Int(Rnd() * (n1 - i + 1)) + i
  133.         gd = sj(r): sj(r) = sj(i): sj(i) = gd
  134.         sj1(i) = sj(i): sj2(i) = ""
  135.     Next i
  136. Next ii



  137. '输出结果至工作表
  138. Range("f22").Value = minyl
  139. Range("f23").Value = minylgs
  140. Range("f24").Value = Format(Sqr(IIf(pd, ylfc1, maxylfc)) / minylgs, "0.00")
  141. ReDim jgjs(1 To n1) '结果计数
  142. For i = 1 To n1 '对结果字串中连接的下料规格做从大到小排序处理
  143.     If jg(i) <> "" Then
  144.         brr = Split(jg(i), "+")
  145.         For j = 0 To UBound(brr) - 1
  146.             For k = j + 1 To UBound(brr)
  147.                 If brr(j) < brr(k) Then gd = brr(k): brr(k) = brr(j): brr(j) = gd
  148.             Next k
  149.         Next j
  150.         jg(i) = ""
  151.         For j = 0 To UBound(brr)
  152.             jg(i) = jg(i) & "+" & brr(j)
  153.         Next j
  154.     End If
  155. Next i
  156. For i = 1 To n1 - 1 '去重并计数
  157.     If jg(i) <> "" Then
  158.         For j = i + 1 To n1
  159.             If jg(j) = jg(i) Then jgjs(i) = jgjs(i) + 1: jg(j) = ""
  160.         Next j
  161.     End If
  162. Next i
  163. h = js + 2 '行
  164. For i = 1 To n1
  165.     If jg(i) <> "" Then
  166.         Cells(h, "i").Value = jgjs(i) + 1
  167.         sxgs = sxgs + jgjs(i) + 1
  168.         brr = Split(jg(i), "+")
  169.         l = 11 'K列
  170.         For j = 1 To UBound(brr)
  171.             Cells(h, l).Value = brr(j)
  172.             l = l + 1
  173.         Next j
  174.         h = h + 1
  175.     End If
  176. Next i
  177. Range("f21").Value = sxgs
  178. MsgBox "用时:" & Format(Timer - t, "0.0000") & "秒。", , "友情提示"
  179. End Sub
复制代码
  为了查看下行数,只有一个过程,没有复杂的调用。

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-30 08:58 | 显示全部楼层
  下面是关于如何实现“优化”的三条标准的代码,是我的代码的第二个核心,也是自认为比法师附件强的地方:

  1.     If yl <= minyl And ylgs <= minylgs Then '记录相对较优方案:余料少、余料根数少
  2.         If ylfc > maxylfc Then '余料方差大
  3.             For i = 1 To n1 '结果出口1
  4.                 jg(i) = sj2(i)
  5.             Next i
  6.             ylfc1 = ylfc '用于正确输出结果
  7.             pd = True
  8.         End If
  9.         If ylgs < minylgs Then
  10.             maxylfc = ylfc '★余料根数减少时重新记录最大余料方差★
  11.             For i = 1 To n1 '结果出口2
  12.                 jg(i) = sj2(i)
  13.             Next i
  14.             pd = False
  15.         End If
  16.         minyl = yl: minylgs = ylgs
  17.     End If
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-10-30 09:05 | 显示全部楼层
  三条优化标准,不是简单的并列关系,也不是简单的顺序关系,总体上应当是这样:
  首先要实现余料总长度最少(终极目标是实现“理想余料”);
  其次是,该余料总长度有许多种分布,要找出“余数根数最少”的所有组合;
  最后,在上述组合中挑出“余料方差最大”的组合,这样,估计在大量随机下,将愈来愈接近“最优方案”!

  上述2、3步的作用,是为了共同实现“余料余大料且根数少”的目标!

  有兴趣的可以进一步研究下,上述2、3两条标准的逻辑关系甚是复杂,我在测试中反复在这出错,所以大家可以看到,我的实现三条标准的代码处理比较奇怪、复杂,就是这个原因。简单地说:因为在余料根数大的时候,往往会得到更大的方差,必须要先完成余料最小根数的寻找后再比较方差。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

关闭

最新热点上一条 /1 下一条

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

GMT+8, 2024-4-25 21:38 , Processed in 0.034183 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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