ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[分享] 求排列组合结果的自定义函数

[复制链接]

TA的精华主题

TA的得分主题

发表于 2014-12-18 17:45 | 显示全部楼层
本帖已被收录到知识树中,索引项:其他结构和算法
香川群子 发表于 2013-9-17 14:23
2013年最新研究成果:

世上最快速度的Excel VBA 组合算法核心部分:计算下面1千万的组合只要1.723秒。( ...

代码大致看明白了,速度提高余地还蛮大的哦~

TA的精华主题

TA的得分主题

发表于 2014-12-19 10:37 | 显示全部楼层
本帖最后由 lee1892 于 2014-12-19 10:39 编辑

51 楼代码改进后的效果,由2.1秒加速到1.6秒不到,20%+
  1. ========================================
  2. Chose 13 From 27 Test #1:
  3.     Original From 香川: 2.125s 20058300
  4. Change Inner DO to FOR: 1.953s 20058300
  5.          Remove One IF: 1.656s 20058300
  6.        Remove Outer DO: 1.578s 20058300
  7. Final Work by Lee1892: 1.547s 20058300
  8. ========================================
  9. Chose 13 From 27 Test #2:
  10.     Original From 香川: 2.109s 20058300
  11. Change Inner DO to FOR: 1.969s 20058300
  12.          Remove One IF: 1.656s 20058300
  13.        Remove Outer DO: 1.578s 20058300
  14. Final Work by Lee1892: 1.547s 20058300
  15. ========================================
  16. Chose 13 From 27 Test #3:
  17.     Original From 香川: 2.109s 20058300
  18. Change Inner DO to FOR: 1.953s 20058300
  19.          Remove One IF: 1.641s 20058300
  20.        Remove Outer DO: 1.563s 20058300
  21. Final Work by Lee1892: 1.563s 20058300
  22. ========================================
  23. Chose 13 From 27 Test #4:
  24.     Original From 香川: 2.156s 20058300
  25. Change Inner DO to FOR: 2.031s 20058300
  26.          Remove One IF: 1.641s 20058300
  27.        Remove Outer DO: 1.609s 20058300
  28. Final Work by Lee1892: 1.594s 20058300
  29. ========================================
  30. Chose 13 From 27 Test #5:
  31.     Original From 香川: 2.141s 20058300
  32. Change Inner DO to FOR: 2.000s 20058300
  33.          Remove One IF: 1.656s 20058300
  34.        Remove Outer DO: 1.609s 20058300
  35. Final Work by Lee1892: 1.563s 20058300
复制代码
代码改进的过程如下:
  1. Private Sub TestSpeed()
  2.     Dim i%, m&, n&
  3.     m = 27
  4.     n = 13
  5.     For i = 1 To 5
  6.         Debug.Print String(40, "=")
  7.         Debug.Print "Chose " & n & " From " & m & " Test #" & i & ":"
  8.         Debug.Print "    Original From 香川: "; Combin_1(m, n)
  9.         Debug.Print "Change Inner DO to FOR: "; Combin_2(m, n)
  10.         Debug.Print "         Remove One IF: "; Combin_3(m, n)
  11.         Debug.Print "       Remove Outer DO: "; Combin_4(m, n)
  12.         Debug.Print " Final Work by Lee1892: "; Combin_Final(m, n)
  13.     Next
  14. End Sub
  15. Function Combin_1(ByVal m&, ByVal n&)
  16.     Dim i&, j&, k&, tms!
  17.     tms = Timer
  18.     ReDim a&(1 To n)
  19.     For j = 1 To n - 1
  20.         a(j) = j
  21.     Next
  22.     k = 0: i = n - 1 ': j = n
  23.     Do
  24.         For i = i + 1 To m
  25.             'a(j) = i
  26.             k = k + 1
  27.         Next
  28.         For j = j - 1 To 1 Step -1
  29.             i = a(j) + 1: a(j) = i
  30.             If i = m - n + j Then
  31.                 k = k + 1
  32.             Else
  33.                 j = j + 1
  34.                 Do Until j = n
  35.                     i = i + 1: a(j) = i: j = j + 1
  36.                 Loop
  37.                 If i = m Then Exit Do Else Exit For
  38.             End If
  39.         Next
  40.     Loop Until j = 0
  41.     Erase a
  42.     Combin_1 = Format(Timer - tms, "0.000s ") & k
  43. End Function
  44. Function Combin_2(ByVal m&, ByVal n&)
  45.     Dim i&, j&, k&, tms!, p&
  46.     tms = Timer
  47.     ReDim a&(1 To n)
  48.     For j = 1 To n ' - 1
  49.         a(j) = j
  50.     Next
  51.     k = 0: i = n - 1: j = n
  52.     Do
  53.         For i = i + 1 To m
  54.             'a(n) = i
  55.             k = k + 1
  56.         Next
  57.         For j = j - 1 To 1 Step -1
  58.             i = a(j) + 1: a(j) = i
  59.             If i = m - n + j Then
  60.                 k = k + 1
  61.             Else
  62.                 'j = j + 1
  63.                 'Do Until j = n
  64.                 For p = j + 1 To n - 1
  65.                     i = i + 1: a(p) = i ': j = j + 1
  66.                 Next
  67.                 'Loop
  68.                 j = n
  69.                 If i = m Then Exit Do Else Exit For
  70.             End If
  71.         Next
  72.     Loop Until j = 0
  73.     Erase a
  74.     Combin_2 = Format(Timer - tms, "0.000s ") & k
  75. End Function
  76. Function Combin_3(ByVal m&, ByVal n&)
  77.     Dim i&, j&, k&, tms!, p&
  78.     tms = Timer
  79.     ReDim a&(1 To n)
  80.     For j = 1 To n
  81.         a(j) = j
  82.     Next
  83.     'k = 0: i = n - 1: j = n
  84.     If m = n Then
  85.         k = 1
  86.         GoTo EXIT_FUNC
  87.     End If
  88.     i = n - 1
  89.     Do
  90.         For i = i + 1 To m
  91.             'a(n) = i
  92.             k = k + 1
  93.         Next
  94.         'For j = j - 1 To 1 Step -1
  95.         For j = n - 1 To 1 Step -1
  96.             i = a(j) + 1: a(j) = i
  97.             If i = m - n + j Then
  98.                 k = k + 1
  99.             Else
  100.                 'j = j + 1
  101.                 'Do Until j = n
  102.                 For p = j + 1 To n - 1
  103.                     i = i + 1: a(p) = i ': j = j + 1
  104.                 Next
  105.                 'Loop
  106.                 'If i = m Then Exit Do Else Exit For
  107.                 Exit For
  108.             End If
  109.         Next
  110.     Loop Until j = 0
  111. EXIT_FUNC:
  112.     Erase a
  113.     Combin_3 = Format(Timer - tms, "0.000s ") & k
  114. End Function
  115. Function Combin_4(ByVal m&, ByVal n&)
  116.     Dim i&, j&, k&, tms!, p&
  117.     tms = Timer
  118.     ReDim a&(1 To n)
  119.     For j = 1 To n
  120.         a(j) = j
  121.     Next
  122.     If m = n Then
  123.         k = 1
  124.         GoTo EXIT_FUNC
  125.     End If
  126.     'k = 0: i = n - 1: j = n
  127.     'Do
  128.         'For i = i + 1 To m
  129.         For i = n To m
  130.             'a(n) = i
  131.             k = k + 1
  132.         Next
  133.         'For j = j - 1 To 1 Step -1
  134.         For j = n - 1 To 1 Step -1
  135.             i = a(j) + 1: a(j) = i
  136.             If i = m - n + j Then
  137.                 k = k + 1
  138.             Else
  139.                 'j = j + 1
  140.                 'Do Until j = n
  141.                 For p = j + 1 To n - 1
  142.                     i = i + 1: a(p) = i ': j = j + 1
  143.                 Next
  144.                 'Loop
  145.                 For i = i + 1 To m
  146.                     'a(n) = i
  147.                     k = k + 1
  148.                 Next
  149.                 j = n
  150.                 'If i = m Then Exit Do Else Exit For
  151.             End If
  152.         Next
  153.     'Loop Until j = 0
  154. EXIT_FUNC:
  155.     Erase a
  156.     Combin_4 = Format(Timer - tms, "0.000s ") & k
  157. End Function
  158. Function Combin_Final(ItemCount As Long, Number As Long)
  159.     Dim i&, j&, nInd&, nCombCnt&, aComb&(), rTime!
  160.     rTime = Timer
  161.     If ItemCount < 0 Then Err.Raise 30001
  162.     If Number < 0 Then Err.Raise 30002
  163.     If Number > ItemCount Then Err.Raise 30003
  164.     If Number = 0 Then
  165.         nCombCnt = 1
  166.         GoTo EXIT_FUNC
  167.     End If
  168.     ReDim aComb(1 To Number)
  169.     For i = 1 To Number
  170.         aComb(i) = i
  171.     Next
  172.     If ItemCount = Number Then
  173.         nCombCnt = 1
  174.         GoTo EXIT_FUNC
  175.     End If
  176.     For i = Number To ItemCount
  177.         'aComb(Number) = i
  178.         nCombCnt = nCombCnt + 1
  179.         'Output combination here
  180.     Next
  181.     For i = Number - 1 To 1 Step -1
  182.         nInd = aComb(i) + 1
  183.         aComb(i) = nInd
  184.         If nInd = ItemCount - Number + i Then
  185.             nCombCnt = nCombCnt + 1
  186.             'Output combination here
  187.         Else
  188.             For j = i + 1 To Number - 1
  189.                 nInd = nInd + 1
  190.                 aComb(j) = nInd
  191.             Next
  192.             For nInd = nInd + 1 To ItemCount
  193.                 'aComb(Number) = nInd
  194.                 nCombCnt = nCombCnt + 1
  195.                 'Output combination here
  196.             Next
  197.             i = Number
  198.         End If
  199.     Next
  200. EXIT_FUNC:
  201.     Erase aComb
  202.     Combin_Final = Format(Timer - rTime, "0.000s ") & nCombCnt
  203. End Function
复制代码

点评

我64楼改进版又可提升约 10%的速度了。  发表于 2014-12-19 13:43

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-12-19 11:13 | 显示全部楼层
lee1892 发表于 2014-12-19 10:37
51 楼代码改进后的效果,由2.1秒加速到1.6秒不到,20%+代码改进的过程如下:

看明白了……把升序过程又进一步细化处理了。

我原来是2个地方有输出组合结果,你现在是3个地方输出结果了。

呵呵。

点评

如果事先算出 C(m,n) 的话,应该可以再改写成 For i=1 To C(m,n) ... Next 结构的,输出应该可以放在一起  发表于 2014-12-19 13:11

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-12-19 13:38 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
lee1892 发表于 2014-12-19 10:37
51 楼代码改进后的效果,由2.1秒加速到1.6秒不到,20%+代码改进的过程如下:

呵呵,还不是最终版。

根据你的思路我又改了改,速度更快!

变量名简化版
  1. Function Combin_6(m&, n&)
  2.     If n >= m Or n < 2 Then Exit Function
  3.    
  4.     Dim i&, j&, j2&, k&, tms#
  5.     tms = Timer
  6.    
  7.     ReDim a&(1 To n), b&(1 To n)
  8.     For j = 1 To n - 1
  9.         a(j) = j: b(j) = m - n + j
  10.     Next
  11.     a(n - 1) = n - 2
  12.    
  13.     For j = n - 1 To 1 Step -1
  14.         i = a(j) + 1: a(j) = i
  15.         If i = b(j) Then
  16.             k = k + 1 'Output
  17.         Else
  18.             For j2 = j + 1 To n - 1
  19.                 i = i + 1: a(j2) = i
  20.             Next
  21.             j = n
  22.             For i = i + 1 To m
  23. '                a(n) = i
  24.                 k = k + 1 'Output
  25.             Next
  26.         End If
  27.     Next
  28.     Combin_6 = Format(Timer - tms, "0.000s ") & k
  29. End Function
复制代码
下面是变量名英文简要版(这个就是所谓自文档化?!)
我看着字母多就觉得还是麻烦。
  1. Function Combin_7(m_ItemCount&, n_CombinNumber&) 'by kagawa 2014/12/19
  2.     If n_CombinNumber >= m_ItemCount Or n_CombinNumber < 2 Then Exit Function
  3.    
  4.     Dim i_CombinValue&, j_DownIndex&, j2_UpIndex&, k_CombinCount&, tms#
  5.     tms = Timer
  6.    
  7.     ReDim a_Value&(1 To n_CombinNumber), b_Limit&(1 To n_CombinNumber)
  8.     For j_DownIndex = 1 To n_CombinNumber - 1
  9.         a_Value(j_DownIndex) = j_DownIndex
  10.         b_Limit(j_DownIndex) = m_ItemCount - n_CombinNumber + j_DownIndex
  11.     Next
  12.     a_Value(n_CombinNumber - 1) = n_CombinNumber - 2
  13.    
  14.     For j_DownIndex = n_CombinNumber - 1 To 1 Step -1
  15.         i_CombinValue = a_Value(j_DownIndex) + 1
  16.         a_Value(j_DownIndex) = i_CombinValue
  17.         If i_CombinValue = b_Limit(j_DownIndex) Then
  18.             k_CombinCount = k_CombinCount + 1 'Output Here while Last_IndexValue=LimitValue
  19.         Else
  20.             For j2_UpIndex = j_DownIndex + 1 To n_CombinNumber - 1
  21.                 i_CombinValue = i_CombinValue + 1
  22.                 a_Value(j2_UpIndex) = i_CombinValue
  23.             Next
  24.             j_DownIndex = n_CombinNumber
  25.             For i_CombinValue = i_CombinValue + 1 To m_ItemCount
  26. '                a_Value(n_CombinNumber) = i_CombinValue
  27.                 k_CombinCount = k_CombinCount + 1 'Output Here while End_IndexValue Change
  28.             Next
  29.         End If
  30.     Next
  31.     Combin_7 = Format(Timer - tms, "0.000s ") & k_CombinCount
  32. End Function
复制代码
最后,是汉字说明的变量自文档化:
  1. Function Combin_8(m組合元素個数&, n組合抽取数&)
  2.     If n組合抽取数 >= m組合元素個数 Or n組合抽取数 < 2 Then Exit Function
  3.    
  4.     Dim i組合値&, j組合位置降序&, j1組合位置昇序&, k組合計数&, tms#
  5.     tms = Timer
  6.    
  7.     ReDim a組合値記録&(1 To n組合抽取数), b組合値上限&(1 To n組合抽取数)
  8.     For j1組合位置昇序 = 1 To n組合抽取数 - 1
  9.         a組合値記録(j1組合位置昇序) = j1組合位置昇序
  10.         b組合値上限(j1組合位置昇序) = m組合元素個数 - n組合抽取数 + j1組合位置昇序
  11.     Next
  12.     a組合値記録(n組合抽取数 - 1) = n組合抽取数 - 2
  13.    
  14.     For j組合位置降序 = n組合抽取数 - 1 To 1 Step -1
  15.         i組合値 = a組合値記録(j組合位置降序) + 1
  16.         a組合値記録(j組合位置降序) = i組合値
  17.         If i組合値 = b組合値上限(j組合位置降序) Then
  18.             k組合計数 = k組合計数 + 1 'Output
  19.         Else
  20.             For j1組合位置昇序 = j組合位置降序 + 1 To n組合抽取数 - 1
  21.                 i組合値 = i組合値 + 1
  22.                 a組合値記録(j1組合位置昇序) = i組合値
  23.             Next
  24.             j組合位置降序 = n組合抽取数
  25.             For i組合値 = i組合値 + 1 To m組合元素個数
  26. '                a組合値記録(n組合抽取数) = i組合値
  27.                 k組合計数 = k組合計数 + 1 'Output
  28.             Next
  29.         End If
  30.     Next
  31.     Combin_8 = Format(Timer - tms, "0.000s ") & k組合計数
  32. End Function
复制代码

点评

第一个速度的提升更多可能是来自于单变量名的缘故。  发表于 2014-12-19 14:00
新加的这个数组b不见得能有多大好处,期望的好处是减少了加减的计算,但坏处是增加了额外的变量询址。循环体内增加一个变量都会带来速度下降的影响。  发表于 2014-12-19 13:59

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-12-19 15:45 | 显示全部楼层
本帖最后由 香川群子 于 2014-12-19 15:57 编辑
lee1892 发表于 2014-12-19 10:37
51 楼代码改进后的效果,由2.1秒加速到1.6秒不到,20%+代码改进的过程如下:

【llee1892  新加的这个数组b不见得能有多大好处,期望的好处是减少了加减的计算,但坏处是增加了额外的变量询址。循环体内增加一个变量都会带来速度下降的影响。  发表于 2014-12-19 13:59】

那好,这样改一下:
  1. Function Combin_6(m&, n&)
  2.     If n >= m Or n < 2 Then Exit Function
  3.    
  4.     Dim i&, i2&, j&, j2&, k&, tms#
  5.     tms = Timer
  6.    
  7.     ReDim a&(1 To n)
  8.     For j = 1 To n - 1
  9.         a(j) = j: b(j) = m - n + j
  10.     Next
  11.     a(n - 1) = n - 2
  12.     i2 = m - 1
  13.    
  14.     For j = n - 1 To 1 Step -1
  15.         i = a(j) + 1: a(j) = i
  16.         If i = i2 Then ' i2 = m - n + j
  17.             k = k + 1 'Output
  18.             i2 = i2 - 1
  19.         Else
  20.             For j2 = j + 1 To n - 1
  21.                 i = i + 1: a(j2) = i
  22.             Next
  23.             j = n: i2 = m - 1
  24.             For i = i + 1 To m
  25. '                a(n) = i
  26.                 k = k + 1 'Output
  27.             Next
  28.         End If
  29.     Next
  30.     Combin_6 = Format(Timer - tms, "0.000s ") & k
  31. End Function
复制代码
增加一个【组合上限值指针 i2】  或【i2_CombinMaxValue】 或 【i2組合値上限】

…………
可是比较发现,还是用数组b记录上限指针的方法速度更快一些。

TA的精华主题

TA的得分主题

发表于 2014-12-19 17:08 | 显示全部楼层
香川群子 发表于 2014-12-19 15:45
【llee1892  新加的这个数组b不见得能有多大好处,期望的好处是减少了加减的计算,但坏处是增加了额外的变 ...

我觉得这些都是小改动了,倒是如果C(m,n)算已知条件的前提下,改成 For i=1 to C(m,n)...Next这样的结构很可能会提速

TA的精华主题

TA的得分主题

发表于 2014-12-19 19:53 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
香川群子 发表于 2013-9-17 16:42
组合算法的汇总

研究学习当中,当组合数据量大时,输出会出错呀,如果数据量稍大点,内存是不是也会不够用,这是不是在做排列组合最大的问题!
头像被屏蔽

TA的精华主题

TA的得分主题

发表于 2015-4-15 09:58 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

TA的精华主题

TA的得分主题

发表于 2016-6-16 15:16 | 显示全部楼层
香川老师能不能帮我看看这个多列组合的例子怎么实现?
http://club.excelhome.net/thread-1284069-1-1.html

TA的精华主题

TA的得分主题

发表于 2016-12-3 23:20 | 显示全部楼层
最近在筛选满足要求的组合,问题很多,可否指教
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-11-17 05:45 , Processed in 0.034418 second(s), 7 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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