ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[原创] 史上最酷的Excel VBA 组合算法 代码

  [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-8-29 00:58 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖已被收录到知识树中,索引项:其他结构和算法
本帖最后由 香川群子 于 2013-8-29 08:47 编辑

  1. Sub test2()
  2.     Dim m&, n&
  3.     m = 26
  4.     For n = 1 To m
  5.         MsgBox CombinDo(m, n) & vbCr & CombinDo2(m, n)
  6.     Next
  7. End Sub
  8. Function CombinDo2(ByVal m&, ByVal n&)
  9.     Dim i&, j&, k&
  10.     tms = Timer
  11.    
  12.     n = n - 1: ReDim a&(n)
  13.     For j = 0 To n - 1
  14.         a(j) = 1 + j
  15.     Next
  16.     If 1 + n = m Then a(n) = m: CombinDo2 = "0.0000s  Do2 Combin(" & m & "," & 1 + n & ")= 1": Exit Function
  17.    
  18.     i = n ': j = n: k = 0
  19.     Do
  20.         For i = i + 1 To m
  21. '            a(j) = i
  22.             k = k + 1
  23.         Next
  24.         
  25.         For j = j - 1 To 0 Step -1
  26.             i = a(j) + 1
  27.             a(j) = i
  28.             If i = m - n + j Then
  29.                 k = k + 1
  30.             Else
  31.                 j = j + 1
  32.                 Do Until j = n
  33.                     i = i + 1
  34.                     a(j) = i
  35.                     j = j + 1
  36.                 Loop
  37.                 If i < m Then Exit For
  38.             End If
  39.         Next
  40.     Loop Until j = -1
  41.     CombinDo2 = Format(Timer - tms, "0.0000s ") & " Do2 Combin(" & m & "," & 1 + n & ")= " & k
  42.    
  43. End Function
  44. Function CombinDo(ByVal m&, ByVal n&)
  45.     Dim i&, j&, k&, r&
  46.     If n = 1 Or n = m Then CombinDo = "0.0000s  Do  Combin(" & m & "," & n & ")= " & IIf(n = 1, m, 1): Exit Function
  47.     tms = Timer
  48.    
  49.     n = n - 1: ReDim a&(n)
  50.     For j = 0 To n - 1
  51.         a(j) = 1 + j
  52.     Next
  53.    
  54.     i = n ': j = n: k = 0
  55.     Do
  56.         i = i + 1: a(j) = i
  57.         
  58.         If j = n Then
  59.             k = k + 1
  60. '            If i = m Then If 1 + n = m Or n = 0 Then Exit Do Else j = j - 1: i = a(j)
  61.             If i = m Then j = j - 1: i = a(j)
  62.         ElseIf i = m - n + j Then
  63.             k = k + 1
  64.             If j = 0 Then Exit Do Else j = j - 1: i = a(j)
  65.         Else
  66.             j = j + 1
  67.         End If
  68.     Loop
  69.     CombinDo = Format(Timer - tms, "0.0000s ") & " Do  Combin(" & m & "," & 1 + n & ")= " & k
  70. End Function
复制代码

TA的精华主题

TA的得分主题

发表于 2013-8-29 08:30 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
新手,看得有点晕乎乎的感觉,到底从那入手

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-8-29 15:40 | 显示全部楼层
代码又做了些改进,居然又提高了20%的速度。
  1. Sub GetCombinMid2()
  2.     Dim i&, j&, k&, l&, m&, n&, s$
  3.     m = [a1].End(4).Row: sj = [a1].Resize(m): n = [b1]
  4.    
  5.     tms = Timer
  6.     If n = m Then [d1] = Join(Application.Transpose(sj), ""): Exit Sub
  7.    
  8.     For j = 1 To m
  9.         If Len(sj(j, 1)) > l Then l = Len(sj(j, 1))
  10.     Next
  11.     ReDim sj1$(1 To m)
  12.     s = String(l, " ")
  13.     For j = 1 To m
  14.         sj1(j) = Right(s & sj(j, 1), l)
  15.     Next
  16.    
  17.     k = Application.Combin(m, n)
  18.     s = String(l * n, " ")
  19.     n = n - 1: ReDim a&(n)
  20.     For j = 0 To n - 1
  21.         a(j) = 1 + j
  22.         Mid(s, j * l + 1, l) = sj1(1 + j)
  23.     Next
  24.    
  25.     ReDim jg$(1 To k, 1 To 1)
  26.    
  27.     i = n: k = 0
  28.     Do
  29.         For i = i + 1 To m
  30.             'a(j) = i
  31.             Mid(s, j * l + 1, l) = sj1(i)
  32.             k = k + 1: jg(k, 1) = s
  33.         Next
  34.         
  35.         For j = j - 1 To 0 Step -1
  36.             i = a(j) + 1: a(j) = i
  37.             Mid(s, j * l + 1, l) = sj1(i)
  38.             If i = m - n + j Then
  39.                 k = k + 1: jg(k, 1) = s
  40.             Else
  41.                 j = j + 1
  42.                 Do Until j = n
  43.                     i = i + 1: a(j) = i
  44.                     Mid(s, j * l + 1, l) = sj1(i)
  45.                     j = j + 1
  46.                 Loop
  47.                 If i < m Then Exit For
  48.             End If
  49.         Next
  50.     Loop Until j = -1
  51.     MsgBox Format(Timer - tms, "0.000s kagawa CombinDoMid2 ") & k
  52.    
  53.     If k < 65536 Then [d:d] = "": [d1].Resize(k) = jg: [d1].EntireColumn.AutoFit
  54.     Erase jg
  55.    
  56. End Sub

复制代码

Combin_kagawa.zip

9.48 KB, 下载次数: 184

TA的精华主题

TA的得分主题

发表于 2013-9-23 22:37 | 显示全部楼层
测试了这样一组数据:0、10、3、8、1、2、6、4、0、0,抽取3个时,貌似有误。
请测试!

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-9-24 10:25 | 显示全部楼层
本帖最后由 香川群子 于 2013-9-24 10:47 编辑
学不完用不尽 发表于 2013-9-23 22:37
测试了这样一组数据:0、10、3、8、1、2、6、4、0、0,抽取3个时,貌似有误。
请测试!


我的代码是不可能有错误的。是你的要求有问题。

你的10个原始数据中0重复了3次……你想要得到什么样的组合结果呢?

根据要求不同,需要不同的代码。


…………
10个元素抽取3个应该得到 =Combin(10,3)=120 组合,或者 =Permut(10,3)=720 排列组合

但如果需要排除3个重复的0,则只有64个不重复组合结果,或者274个不重复排列结果。

下面是64组不同的组合:
0,0,0;0,0,1;0,0,2;0,0,3;0,0,4;0,0,6;0,0,8;0,0,10;0,1,2;0,1,3;0,1,4;0,1,6;0,1,8;0,1,10;0,2,3;0,2,4;
0,2,6;0,2,8;0,2,10;0,3,4;0,3,6;0,3,8;0,3,10;0,4,6;0,4,8;0,4,10;0,6,8;0,6,10;0,8,10;1,2,3;1,2,4;1,2,6;
1,2,8;1,2,10;1,3,4;1,3,6;1,3,8;1,3,10;1,4,6;1,4,8;1,4,10;1,6,8;1,6,10;1,8,10;2,3,4;2,3,6;2,3,8;2,3,10;
2,4,6;2,4,8;2,4,10;2,6,8;2,6,10;2,8,10;3,4,6;3,4,8;3,4,10;3,6,8;3,6,10;3,8,10;4,6,8;4,6,10;4,8,10;6,8,10




补充内容 (2014-4-18 14:59):
更正:10个元素中3个相同,其余7个各自不同,那么不重复的排列组合共有358个。之前0值处理有误导致结果错误。

TA的精华主题

TA的得分主题

发表于 2013-9-24 15:58 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
牛逼的群子

TA的精华主题

TA的得分主题

发表于 2013-9-24 17:14 | 显示全部楼层
香川群子 发表于 2013-7-15 14:07
自己顶一次。

香川群子的组合算法最棒!

的确很棒!!!学习中!

TA的精华主题

TA的得分主题

 楼主| 发表于 2013-9-24 17:30 | 显示全部楼层
彭希仁 发表于 2013-9-24 15:58
牛逼的群子

呵呵,当抽取数n>m/2(元素总数一半以上),尤其是接近m时,

使用我的算法,比单纯的循环速度都快。

本来我自己都认为循环是组合算法速度的极限了。


…………
彭版有空研究一下我的算法思路。

关键是,我采用的直接升位法,

比如,8个元素中抽取5个时, 对于 1,4,5,6,7 状态,我可以直接一步升位一次:
   1,4,5,6,7
→ 1,4,5,6,8
→ 1,4,5,7,8
→ 1,4,6,7,8
→ 1,5,6,7,8

然后才是:
→ 2,3,4,5,6

这么做会节省很多时间的。


另外一个算法速度快的原因是:
i 变量(元素位置)和j 变量(组合位置)的直接计算高效使用,几乎不进行数组件元素的计算。


所以,目前世界上Excel VBA代码中,还没有比我更好的算法。





点评

一般组合考虑时总是前进的思路,您这儿倒是“后退”的思路,本楼提示有助于理解Do组合代码的思想……  发表于 2014-10-21 21:05

TA的精华主题

TA的得分主题

发表于 2013-9-24 21:09 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
香川群子 发表于 2013-9-24 10:25
我的代码是不可能有错误的。是你的要求有问题。

你的10个原始数据中0重复了3次……你想要得到什么样 ...

第二次运行又正确了,不好意思啊!

TA的精华主题

TA的得分主题

发表于 2013-9-26 22:06 | 显示全部楼层
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-5-3 05:50 , Processed in 0.047207 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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