ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[求助] R*C数组共计M个选N的组合

[复制链接]

TA的精华主题

TA的得分主题

发表于 2023-12-28 21:26 | 显示全部楼层 |阅读模式
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
先已知一个R行C列的一个二维数组。总数量为M=R*C,从中选N个进行组合。有两个约束条件,选取出来的组合在同一行内不得超过n1个,同一列内不得超过n2个。

以下为举例,比如是8行4列的32个数字,选取6个生成组合。约束条件是:同一行不能超过2个,同一列不能超过3个。
image.png

用穷举的方法循环判断代码如下:
  1. Sub GetCombin()
  2.     Dim Source, Result
  3.     Dim i1&, i2&, i3&, i4&, i5&, i6&
  4.     Dim j&, k&, M&, N&
  5.     Const Symbol As String = ";"
  6.     Source = [=SEQUENCE(,32)]
  7.     M = 32
  8.     N = 6
  9.     k = Application.WorksheetFunction.Combin(M, N)
  10.     ReDim Result(1 To k, 0)
  11.    
  12.     For i1 = 1 To M - 5
  13.     For i2 = i1 + 1 To M - 4
  14.     For i3 = i2 + 1 To M - 3
  15.     For i4 = i3 + 1 To M - 2
  16.     For i5 = i4 + 1 To M - 1
  17.     For i6 = i5 + 1 To M - 0
  18.         If CheckNum(i1, i2, i3, i4, i5, i6) Then
  19.         j = j + 1
  20.         Result(j, 0) = Source(i1) & Symbol & Source(i2) & Symbol & Source(i3)
  21.         Result(j, 0) = Result(j, 0) & Symbol & Source(i4) & Symbol & Source(i5)
  22.         Result(j, 0) = Result(j, 0) & Symbol & Source(i6)
  23.         End If
  24.     Next i6, i5, i4, i3, i2, i1
  25.     Range("a1").Resize(j).Value = Result
  26.     MsgBox j & " of " & k
  27. End Sub
  28. Private Function CheckNum(ParamArray ar()) As Boolean
  29.     Dim r(1 To 8), c(1 To 4)
  30.     For Each i In ar
  31.         j = Int((i + 3) / 4)
  32.         k = (i - 1) Mod 4 + 1
  33.         r(j) = r(j) + 1
  34.         c(k) = c(k) + 1
  35.         If r(j) > 2 Then Exit Function
  36.         If c(k) > 3 Then Exit Function
  37.     Next
  38.     CheckNum = True
  39. End Function
复制代码
(这个代码循环运算量比较大,也不能拓展到任意M和N)优化代码逻辑和速度,怎么处理比较好??

TA的精华主题

TA的得分主题

发表于 2023-12-29 03:41 | 显示全部楼层
本帖最后由 wanghan519 于 2023-12-29 07:23 编辑


论坛里应该有vba递归组合的代码,也就应该可以扩展到更多的行列。。。
没有算法上的优化,只是写起来简单点,用的是网页版金山文档,结果数组是出来了,但发现写入行多了会报错,还是不完善啊。。。
image.jpg

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-12-29 09:38 | 显示全部楼层
PY有库还是方便啊,vba按理说也会有人开发类似的库,估计是太小众哪怕分享了,也没流传开来。

论坛香川老师的递归组合就很好,只不过会用不会改

TA的精华主题

TA的得分主题

发表于 2023-12-29 11:37 | 显示全部楼层
供参考。

micch_二维数组共计M个选N的组合.rar

19.03 KB, 下载次数: 18

micch_二维数组共计M个选N的组合(数组).rar

19.34 KB, 下载次数: 15

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2023-12-29 16:07 | 显示全部楼层
o.png

c1.png

c2.png

条件组合231229.rar

21.37 KB, 下载次数: 16

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2023-12-29 23:46 | 显示全部楼层
image.jpg

RC组合By@今铭昔.zip

63.82 KB, 下载次数: 8

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-12-30 09:57 | 显示全部楼层
一维数组递归的勉强理解,可以抄一下;二维行列递归这个感觉更符合人工处理的逻辑,但是理解起来费劲

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-12-30 11:31 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
交作业,递归是真不好用,还是不太会。   一维数组递归组合勉强会抄了,二维行列同时递归,感觉有点深度搜索或者动态规划,思路能想通,代码不会改。先按一维数组递归组合写一个


image.png

测试模块

  1. ' 有个疑问
  2. ' 如果不读取单元格32个数字,直接用默认1-32组成数组进行组合,速度 0.5~
  3. ' 如果读取单元格8行4列的数字,作为数据源数组进行组合,速度 1.1~
  4. ' 而实际时间的计算是从数据源数组生成后开始计时,也就是组合过程和数据源其实无关
  5. Sub Test()
  6.     Dim ar, r As Long
  7.     With New ClsCombin
  8. '        .Source = Range("c3:f10").Value     ' 注释后会用默认数组,内容一致
  9.         ar = .Result
  10.         If .Cnt < 100 Then r = .Cnt Else r = 100
  11.     End With
  12.     Columns(1).ClearContents
  13.     Range("a1").Resize(r).Value = ar
  14. End Sub



  15. Sub Test1()
  16.     Dim ar, r As Long
  17.     With New ClsCombin
  18.         .Source = Range("m3").CurrentRegion.Value
  19.         .n = Range("k4").Value
  20.         .nRow = Range("k6").Value
  21.         .nColumn = Range("k8").Value
  22.         ar = .Result
  23.         If .Cnt < 100 Then r = .Cnt Else r = 100
  24.         Range("H10").Value = .n & " ; " & .nRow & " ; " & .nColumn & " ; " & .Cnt
  25.         If .Cnt = 0 Then Exit Sub
  26.     End With
  27.     Columns(1).ClearContents
  28.     Range("a1").Resize(r).Value = ar
  29. End Sub
复制代码
类模块
  1. ' 类名称:ClsCombin
  2. ' 组合递归代码
  3. ' 读入一个二维数组和抽取数量,行列约束数量,共4个参数,返回符合条件的组合数组
  4. Option Explicit

  5. Private m_ As Integer           '-- 元素总数量
  6. Private n_ As Integer           '-- 抽取数量

  7. Private nRow_ As Integer        '-- 行约束条件数量
  8. Private nColumn_ As Integer     '-- 列约束条件数量

  9. Private Source_                 '-- 数据源转化为一列
  10. Private Result_                 '-- 结果

  11. Private ubound1_ As Integer     '-- 数组上限
  12. Private ubound2_ As Integer     '-- 数组上限
  13. Private cntMax_ As Long         '-- 组合上限
  14. Private cnt_ As Long            '-- 组合结果数量

  15. Private arrNum_() As Integer    '-- 一维数组(备用)
  16. Private arrRow_() As Integer    '-- 记录行数量
  17. Private arrColumn_() As Integer '-- 记录列数量

  18. Public Property Let m(m0 As Integer)
  19.     m_ = m0
  20. End Property
  21. Public Property Get m() As Integer
  22.     m = m_
  23. End Property
  24. Public Property Let n(n0 As Integer)
  25.     n_ = n0
  26. End Property
  27. Public Property Get n() As Integer
  28.     n = n_
  29. End Property
  30. Public Property Let nRow(nRow0 As Integer)
  31.     nRow_ = nRow0
  32. End Property
  33. Public Property Get nRow() As Integer
  34.     nRow = nRow_
  35. End Property
  36. Public Property Let nColumn(nColumn0 As Integer)
  37.     nColumn_ = nColumn0
  38. End Property
  39. Public Property Get nColumn() As Integer
  40.     nColumn = nColumn_
  41. End Property

  42. Public Property Let Source(Source0 As Variant)  '-- 二维数组为数据源
  43.     ReadSourceArray Source0
  44. End Property
  45. Public Property Get Result() As Variant
  46.     GetCombin
  47.     Result = Result_
  48. End Property
  49. Public Property Get Cnt() As Variant
  50.     Cnt = cnt_
  51. End Property

  52. Public Sub GetCombin()
  53.     Dim tms
  54.     tms = Timer

  55.     cntMax_ = Application.WorksheetFunction.Combin(m_, n_)
  56.     ReDim Result_(1 To cntMax_, 1 To 1)
  57.     cnt_ = 0
  58.     Call Recursion("", 0, 1)
  59.    
  60.     MsgBox cnt_ & vbCrLf & Format(Timer - tms, "0.000s")
  61. End Sub
  62. Private Sub Recursion(s As String, i As Long, t As Long)
  63.     Dim j As Long, ss As String
  64.     For j = i + 1 To m_
  65.         If arrRow_(Source_(j, 2)) < nRow_ Then
  66.         If arrColumn_(Source_(j, 3)) < nColumn_ Then
  67.         If t = n_ Then
  68.                 cnt_ = cnt_ + 1
  69.                 Result_(cnt_, 1) = Mid(s & ";" & Source_(j, 1), 2)
  70.         Else
  71.                 arrRow_(Source_(j, 2)) = arrRow_(Source_(j, 2)) + 1
  72.                 arrColumn_(Source_(j, 3)) = arrColumn_(Source_(j, 3)) + 1
  73.                 Call Recursion(s & ";" & Source_(j, 1), j, t + 1)
  74.                 arrRow_(Source_(j, 2)) = arrRow_(Source_(j, 2)) - 1
  75.                 arrColumn_(Source_(j, 3)) = arrColumn_(Source_(j, 3)) - 1
  76.         End If
  77.         End If
  78.         End If
  79.     Next
  80. End Sub


  81. Private Sub Class_Initialize()
  82.     InitiDefault
  83. End Sub
  84. Private Sub Class_Terminate()
  85. End Sub

  86. Private Sub InitiDefault()
  87.     Dim i As Long, j As Long, k As Long
  88.    
  89.     n_ = 6
  90.     nRow_ = 2
  91.     nColumn_ = 3
  92.    
  93.     ubound1_ = 8
  94.     ubound2_ = 4
  95.     m_ = ubound1_ * ubound2_
  96.     ReDim Source_(1 To m_, 1 To 4)
  97.     For i = 1 To ubound1_
  98.     For j = 1 To ubound2_
  99.         k = k + 1
  100.         Source_(k, 1) = k
  101.         Source_(k, 2) = i
  102.         Source_(k, 3) = j
  103.     Next
  104.     Next
  105.     ReDim arrRow_(1 To ubound1_)
  106.     ReDim arrColumn_(1 To ubound2_)
  107. End Sub
  108. Private Sub ReadSourceArray(Source0)
  109.     Dim i As Long, j As Long, k As Long
  110.     ubound1_ = UBound(Source0, 1)
  111.     ubound2_ = UBound(Source0, 2)
  112.     m_ = ubound1_ * ubound2_
  113.    
  114.     ReDim Source_(1 To m_, 1 To 4)
  115.     For i = 1 To ubound1_
  116.     For j = 1 To ubound2_
  117.         k = k + 1
  118.         Source_(k, 1) = Source0(i, j)
  119.         Source_(k, 2) = i
  120.         Source_(k, 3) = j
  121.     Next
  122.     Next
  123.    
  124.     ReDim arrRow_(1 To ubound1_)
  125.     ReDim arrColumn_(1 To ubound2_)
  126.    
  127. End Sub


  128. ''''Private Sub Recursion(s As String, i As Long, t As Long)
  129. ''''    Dim j As Long, ss As String
  130. ''''    dgn = dgn + 1
  131. ''''    For j = i + 1 To m_
  132. ''''        If t = n_ Then
  133. ''''            '--方案1
  134. ''''            If CheckNum(s & ";" & Source_(j, 1)) Then
  135. ''''            cnt_ = cnt_ + 1
  136. ''''            Result_(cnt_, 1) = Mid(s & ";" & Source_(j, 1), 2)
  137. ''''            End If
  138. ''''        Else
  139. ''''            If CheckNum(s & ";" & Source_(j, 1)) Then
  140. ''''            Call Recursion(s & ";" & Source_(j, 1), j, t + 1)
  141. ''''            Else
  142. ''''            End If
  143. ''''        End If
  144. ''''    Next
  145. ''''End Sub
  146. ''''Private Function CheckNum(s0 As String) As Boolean
  147. ''''    Dim ar, br1, br2
  148. ''''    Dim i As Long, j As Long, r As Long, c As Long
  149. ''''    ar = Split(s0, ";")
  150. ''''    ReDim br1(1 To ubound1_)
  151. ''''    ReDim br2(1 To ubound2_)
  152. ''''    For i = 1 To UBound(ar)
  153. ''''        j = ar(i)
  154. ''''        r = Source_(j, 2)
  155. ''''        c = Source_(j, 3)
  156. ''''        br1(r) = 1 + br1(r)
  157. ''''        br2(c) = 1 + br2(c)
  158. ''''        If br1(r) > nRow_ Then Exit Function
  159. ''''        If br2(c) > nColumn_ Then Exit Function
  160. ''''    Next
  161. ''''    CheckNum = True
  162. ''''End Function
复制代码
Combin.zip (29.57 KB, 下载次数: 2)

TA的精华主题

TA的得分主题

 楼主| 发表于 2023-12-30 12:30 | 显示全部楼层

JS代码吧?代码很简洁。

主体部分,就是行和列一起递归,当行抽取数量达到上限时,就结束,没到上限就提取出来组合。列同理。

所以,类似于从  1,1 开始点 到 8,4 结束点,进行路径搜索,搜索的同时记录经过的点,如果同一行或者同一列经过的点超过限制条件,后面的路径就都不走了,退回上一个节点重新向右向下搜索。

总感觉vba也能模拟出来,但是尝试了下,还是没写出来。

TA的精华主题

TA的得分主题

发表于 2023-12-30 12:43 | 显示全部楼层
micch 发表于 2023-12-30 12:30
JS代码吧?代码很简洁。

主体部分,就是行和列一起递归,当行抽取数量达到上限时,就结束,没到上限 ...

image.jpg

RC组合By@今铭昔.zip

106.79 KB, 下载次数: 9

评分

1

查看全部评分

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-6-26 17:18 , Processed in 0.052962 second(s), 19 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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