ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[分享] 香川组合的递归代码

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2012-7-10 20:12 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:递归
有m行n列数据,要求各列中任意抽取一个元素,共n个元素重新组合,求列出所有组合结果。


举例:
A 1 甲
B 2 乙
C 3 丙

得到结果:
A;1;甲
A;1;乙
A;1;丙
A;2;甲
A;2;乙
A;2;丙
A;3;甲
A;3;乙
A;3;丙
B;1;甲
B;1;乙
B;1;丙
B;2;甲
B;2;乙
B;2;丙
B;3;甲
B;3;乙
B;3;丙
C;1;甲
C;1;乙
C;1;丙
C;2;甲
C;2;乙
C;2;丙
C;3;甲
C;3;乙
C;3;丙

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-7-10 20:17 | 显示全部楼层
结果组合实际上有个特点,第k列的元素永远只在第k列中出现。

因此,实际上组合总数=m^n
即,每列元素都是m个中任取一个元素 = m 种变化,
而每增加1列,就多一次乘法。2列就是 m^2 ,3列就是 m^3 ……,


我以前用数组循环代码实现,但代码比较复杂。(当列数不定时,循环次数也不同)

但是,用递归方法写出的代码,却很简单:
  1. Public sj, jg(), m, n, r
  2. Sub 香川组合递归()
  3.     sj = ActiveCell.CurrentRegion: m = UBound(sj): n = UBound(sj, 2): Amn = m ^ n
  4.     If Amn > 65536 Then Exit Sub Else ReDim jg(Amn, 0): r = 0
  5.     Call mndg("", 0, 0)
  6.     ActiveCell.CurrentRegion.Cells(1).offset(m + 3, n).Resize(r) = jg
  7. End Sub
  8. Sub mndg(s$, i, t%)
  9.     If t = n Then
  10.         p = Split(s, ";")
  11.         For j = 1 To n
  12.             jg(r, 0) = jg(r, 0) & ";" & sj(p(j), j)
  13.         Next
  14.         jg(r, 0) = Mid(jg(r, 0), 2): r = r + 1: Exit Sub
  15.     End If
  16.     For j = i + 1 To m
  17.         Call mndg(s & ";" & j, 0, t + 1)
  18.     Next j
  19. End Sub
复制代码

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2012-7-10 20:18 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2012-7-10 20:20 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
香穿裙子又有新作品了。

TA的精华主题

TA的得分主题

发表于 2012-7-10 20:22 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
支持一下            

TA的精华主题

TA的得分主题

发表于 2012-7-10 20:25 | 显示全部楼层
但是数据量大的时候递归会很慢的。

列数不确定应该也不影响循环吧。

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-7-10 20:35 | 显示全部楼层
如果考虑到,原始数据区域中可能包含空白单元格,要剔除,则代码修改如下:
另外,各个组合的所有各列元素的结果也直接输出了。
  1. Public sj, jg(), m, n, r
  2. Sub 香川组合递归()
  3.     sj = ActiveCell.CurrentRegion: m = UBound(sj): n = UBound(sj, 2): Amn = m ^ n
  4.     If Amn > 65536 Then Exit Sub Else ReDim jg(Amn, 1 To n + 1): r = 0
  5.     Call mndg("", 0, 0)
  6.     ActiveCell.CurrentRegion.Cells(1).offset(m + 3).Resize(r, n + 1) = jg
  7. End Sub
  8. Sub mndg(s$, i, t%)
  9.     If t = n Then
  10.         p = Split(s, ";")
  11.         For j = 1 To n
  12.             jg(r, j) = sj(p(j), j)
  13.             jg(r, n + 1) = jg(r, n + 1) & ";" & sj(p(j), j)
  14.         Next
  15.         jg(r, n + 1) = Mid(jg(r, n + 1), 2): r = r + 1: Exit Sub
  16.     End If
  17.     For j = i + 1 To m
  18.         If sj(j, t + 1) <> "" Then Call mndg(s & ";" & j, 0, t + 1)
  19.     Next j
  20. End Sub
复制代码

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2012-7-10 20:45 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-7-10 20:48 | 显示全部楼层
顺便公开一下,标准组合、标准排列的递归代码:
  1. Sub 香川组合递归()
  2.     sj = [a1].CurrentRegion: m = UBound(sj): n = UBound(sj, 2): Amn = m ^ n
  3.     If Amn > 65536 Then Exit Sub Else ReDim jg(Amn, n): r = 0
  4.     Call mndg("", 0, 0)
  5.     [a1].offset(, n + 3).CurrentRegion = "": [a1].offset(, n + 1) = Amn: [a1].offset(, n + 3).Resize(Amn, n + 1) = jg
  6. End Sub
  7. Sub mndg(s$, i, t%)
  8.     If t = n Then
  9.         p = Split(s, ";")
  10.         For j = 1 To n
  11.             jg(r, j) = sj(p(j), j)
  12.             jg(r, 0) = jg(r, 0) & ";" & sj(p(j), j)
  13.         Next
  14.         jg(r, 0) = Mid(jg(r, 0), 2): r = r + 1: Exit Sub
  15.     End If
  16.     For j = i + 1 To m
  17.         If sj(j, t + 1) <> "" Then Call mndg(s & ";" & j, 0, t + 1)
  18.     Next j
  19. End Sub
  20. Sub 排列递归()
  21.     m = [a1].End(4).Row: sj = [a1].Resize(m): n = [b1]
  22.     AP = WorksheetFunction.Permut(m, n): ReDim jg(AP, n): r = 0
  23.     Call pldg("", 0)
  24.     [b3] = AP: [d1].CurrentRegion: [d1].Resize(AP, n + 1) = jg
  25. End Sub
  26. Sub pldg(s$, t%)
  27.     If t = n Then
  28.         p = Split(s, ";")
  29.         For j = 1 To n
  30.             jg(r, j) = sj(p(j), 1)
  31.             jg(r, 0) = jg(r, 0) & ";" & sj(p(j), 1)
  32.         Next
  33.         jg(r, 0) = Mid(jg(r, 0), 2): r = r + 1: Exit Sub
  34.     End If
  35.     For j = 1 To m
  36.         If InStr(s & ";", ";" & j & ";") = 0 Then Call pldg(s & ";" & j, t + 1)
  37.     Next j
  38. End Sub
  39. Sub 组合递归()
  40.     m = [a1].End(4).Row: sj = [a1].Resize(m): n = [b1]
  41.     AC = WorksheetFunction.Combin(m, n): ReDim jg(AC, n): r = 0
  42.     Call zhdg("", 0, 0)
  43.     [b3] = AC: [d1].CurrentRegion = "": [d1].Resize(AC, n + 1) = jg
  44. End Sub
  45. Sub zhdg(s$, i, t%)
  46.     If t = n Then
  47.         p = Split(s, ";")
  48.         For j = 1 To n
  49.             jg(r, j) = sj(p(j), 1)
  50.             jg(r, 0) = jg(r, 0) & ";" & sj(p(j), 1)
  51.         Next
  52.         jg(r, 0) = Mid(jg(r, 0), 2): r = r + 1: Exit Sub
  53.     End If
  54.     For j = i + 1 To m
  55.         Call zhdg(s & ";" & j, j, t + 1)
  56.     Next j
  57. End Sub
复制代码

评分

3

查看全部评分

TA的精华主题

TA的得分主题

发表于 2012-7-10 21:20 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
递归一直都学不会。。 群子老师怎么学的?介绍经验。。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-11-17 00:38 , Processed in 0.046614 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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