Excel精英培训网

 找回密码
 注册
数据透视表40+个常用小技巧,让你一次学会!
楼主: 爱疯

[已解决]这类循环方式是针对什么问题的

[复制链接]
发表于 2012-11-10 00:35 | 显示全部楼层
对于特定例子,可以用1楼代码去写去理解
对于一般情况或者说通用,可能还是得用递归
回复

使用道具 举报

 楼主| 发表于 2012-11-10 11:23 | 显示全部楼层
上清宫主 发表于 2012-11-10 00:35
对于特定例子,可以用1楼代码去写去理解
对于一般情况或者说通用,可能还是得用递归

没具体问题是不好说。
1楼这种方式就象一个工具,所以就想知道这种工具适合解决什么题。从具体例子中体会它的好处。
那就等下次我能举出问题,再问好了,谢谢了!
回复

使用道具 举报

发表于 2012-11-11 14:21 | 显示全部楼层    本楼为最佳答案   
呵呵,我自己来回答。

1. 这样的代码作用,是最基本的,通过循环产生不重复组合的【组合代码】
结果是: =combin(m,n) 的所有组合解。

2. 用VBA代码生成组合结果,方法有很多。
a. 本例所示,是最基本的,或称最标准的数组循环方法。

优点是:
计算速度最快。(其他任何方法,都不可能比它更快!)
第2个优点是,非常直观,便于新手理解和模仿。

缺点是:
通用性不强。即,如果m,n参数需要改变,那么代码就需要重新编写……

但是,这个问题,我有自己的独门暗器解决。
即,我另做了一个生成组合代码的VBA
  1. Sub 自动生成组合代码()
  2.    
  3.     m = Val(InputBox("组合对象个数 m =", "生成组合代码", 10))
  4.     If m = 0 Then Exit Sub
  5.     n = Val(InputBox("抽取个数 n =", "生成组合代码", 3))
  6.     If n = 0 Then Exit Sub
  7.     AC = WorksheetFunction.Combin(m, n)
  8.     MsgBox "组合结果总数 = " & AC
  9.    
  10.     tn = "AutoCombin_" & Format(Date, "yymmdd_") & Format(Time, "hhmm_") & "Macro"
  11.     '本组合代码名称为: 【AutoCombin_yymmdd_hhmm_Macro】,确保每次名称不同
  12.     s = s & "Sub " & tn & "()" & Chr(10) & Chr(10)
  13.     s = s & "tms = Timer" & Chr(10)
  14.     s = s & "m = " & m & " : n = " & n & Chr(10)
  15.     s = s & "ReDim jg(1 To " & AC & ", 0 To n)" & Chr(10)
  16.    
  17.     T1 = "Dim i1%"
  18.     For i = 2 To n
  19.         T1 = T1 & ", i" & i & "%"
  20.     Next
  21.     s = s & T1 & Chr(10)
  22.    
  23.     s = s & "For i1 = 1 To " & m - n + 1 & Chr(10)
  24.    
  25.     T2 = " jg(i, 0) = i1"
  26.     T3 = " jg(i, 1) = i1"
  27.     T4 = "next i" & n
  28.     For i = 2 To n
  29.         s = s & "For i" & i & " = i" & i - 1 & " + 1 To " & m - n + i & Chr(10)
  30.         T2 = T2 & " & "","" & i" & i
  31.         T3 = T3 & " : jg(i, " & i & ") = i" & i
  32.         T4 = T4 & ", i" & n - i + 1
  33.     Next
  34.     s = s & " i = i + 1" & Chr(10)
  35.     s = s & T2 & Chr(10)
  36.     s = s & T3 & Chr(10)
  37.     s = s & T4 & Chr(10)
  38.    
  39.     s = s & "[a1].CurrentRegion.Clear: [a1].Resize(i, n + 1) = jg" & Chr(10)
  40.     s = s & "[a1].Resize(, n + 1).EntireColumn.AutoFit" & Chr(10)
  41.     s = s & "Msgbox i & vbcr & Format(Timer - tms ,""0.000s"")" & Chr(10) & Chr(10)
  42.     s = s & "End Sub" & Chr(10)
  43.    
  44.     '以上为止,自动生成了能够计算combin(m,n)组合的标准循环代码 s
  45.     [a1] = s '输出代码到A1单元格中(代码行之间有换行符)

  46.     ' 如果只需要添加模块到当前工作簿,但不需要自动执行新生成的代码,则:
  47.     Set t = ActiveWorkbook.VBProject.VBComponents.Add(1) '在当前工作簿文件中自动添加模块
  48.     t.CodeModule.AddFromString s '在此新添加的模块中写入此代码
  49.     Exit Sub
  50.     '如果要立即自动执行此组合代码,那么:
  51.     Set t = ThisWorkbook.VBProject.VBComponents.Add(1) '在本代码所属工作簿文件中自动添加模块,
  52.     t.CodeModule.AddFromString s '在此新添加的模块中写入此代码
  53.     Application.Run tn '自动执行此代码
  54.     ThisWorkbook.VBProject.VBComponents.Remove t '执行完之后立即删掉此代码模块→不需要时注释掉
  55.    
  56. End Sub
复制代码
执行上述代码,就能自动生成标准的,生成combin(m,n)解的代码。

例如,执行代码,输入m=5,n=3参数后,自动在当前工作薄内生成一个模块,里面有代码:

Sub AutoCombin_121111_1419_Macro()

tms = Timer
m = 5: n = 3
ReDim jg(1 To 10, 0 To n)
Dim i1%, i2%, i3%
For i1 = 1 To 3
For i2 = i1 + 1 To 4
For i3 = i2 + 1 To 5
i = i + 1
jg(i, 0) = i1 & "," & i2 & "," & i3
jg(i, 1) = i1: jg(i, 2) = i2: jg(i, 3) = i3
Next i3, i2, i1
[a1].CurrentRegion.Clear: [a1].Resize(i, n + 1) = jg
[a1].Resize(, n + 1).EntireColumn.AutoFit
MsgBox i & vbCr & Format(Timer - tms, "0.000s")

End Sub
回复

使用道具 举报

发表于 2012-11-11 14:23 | 显示全部楼层
本帖最后由 香川群子 于 2012-11-11 14:24 编辑

方法2,递归方法生成组合代码:
  1. Dim sj, jg(), m%, n%, k
  2. Sub 组合递归()
  3.     tms = Timer
  4.     m = [a1].End(4).Row: sj = [a1].Resize(m): n = [b1]
  5.     AC = WorksheetFunction.Combin(m, n): ReDim jg(AC, n): k = 0
  6.     Call dgZH("", 0, 0): MsgBox Timer - tms
  7.     [b3] = AC: [d1].CurrentRegion = "": If AC < 65536 Then [d1].Resize(AC, n + 1) = jg
  8. End Sub
  9. Sub dgZH(s$, i%, t%)
  10.     Dim j%
  11.     If t = n Then
  12.         p = Split(s, ";")
  13.         For j = 1 To n
  14.             jg(k, j) = sj(p(j), 1)
  15.             jg(k, 0) = jg(k, 0) & ";" & sj(p(j), 1)
  16.         Next
  17.         jg(k, 0) = Mid(jg(k, 0), 2):
  18.         k = k + 1: Exit Sub
  19.     End If
  20.     For j = i + 1 To m
  21.         Call dgZH(s & ";" & j, j, t + 1)
  22.     Next j
  23. End Sub
复制代码
优点,通用性强,但计算速度稍慢。(递归的缺点,你懂的。)
回复

使用道具 举报

发表于 2012-11-11 15:16 | 显示全部楼层
最后,是数组循环方式的通用代码。
  1. Sub GetCombin()
  2.     tms = Timer
  3.    
  4.     Dim i&, j%, k&, l%, m%, n%, s&
  5.    
  6.     m = [a1].End(4).Row
  7.     n = [b1]
  8.     arr = [a1].Resize(m)
  9.         
  10.     s = Application.Combin(m, n)
  11.     ReDim crr(1 To s, 1 To 1)
  12.    
  13.     ReDim a%(1 To n)
  14.     a(1) = 1
  15.     If n > 1 Then t = arr(1, 1) Else t = ""
  16.     For i = 2 To n - 1
  17.         a(i) = i
  18.         t = t & ";" & arr(i, 1)
  19.     Next
  20.     a(n) = n
  21.    
  22.     For i = 1 To s 'Application.Combin(m, n)
  23.         'k = k + 1: crr(k, 1) = t & ";" & arr(a(n), 1)
  24.         crr(i, 1) = t & ";" & arr(a(n), 1)
  25.         
  26.         a(n) = a(n) + 1
  27.         If a(n) > m Then
  28.             If a(1) > m - n Then GoTo Ext
  29.             
  30.             For j = 2 To n
  31.                 If a(j) >= m - n + j Then l = j - 1: Exit For
  32.             Next j
  33.             
  34.             a(l) = a(l) + 1
  35.             For j = l + 1 To n
  36.                 a(j) = a(j - 1) + 1
  37.             Next j
  38.             
  39.             t = arr(a(1), 1)
  40.             For j = 2 To n - 1
  41.                 t = t & ";" & arr(a(j), 1)
  42.             Next j
  43.             
  44.         End If
  45.     Next i
  46. Ext:
  47.     MsgBox Format(Timer - tms, "0.000s")
  48.     tms = Timer
  49.     [d:d] = ""
  50.     [d1].Resize(s) = crr
  51.     [d1].EntireColumn.AutoFit

  52.     MsgBox Format(Timer - tms, "0.000s")
  53. End Sub
复制代码
比递归速度快,但代码相对复杂很多。


回复

使用道具 举报

发表于 2012-11-11 15:18 | 显示全部楼层
还有,就是利用现成的第3方脚本程序软件如rubby等方法……但是速度超级慢。


所以,如果自己有能力,还是不要用这些第3方程序为好。

回复

使用道具 举报

 楼主| 发表于 2012-11-11 15:31 | 显示全部楼层
香川群子 发表于 2012-11-11 14:23
方法2,递归方法生成组合代码:优点,通用性强,但计算速度稍慢。(递归的缺点,你懂的。)

谢谢群子老师!

群子老师的回复,就像饱和式攻击,特详细、充分,且非常容易查看。
对于排列组合,如何入门,如何学才更有效率?大家都很想知道并学会,但还是得作些准备。群子老师这是这方面的专家,特别希望您能给小结一篇扫盲普及贴,以新手角度,给出指导和建议。
这样,再读群子老师及各位高手们的回复时,就能更好的理解了。不然,那么多回复没理解的话,实在太可惜啦!

回复

使用道具 举报

发表于 2012-11-11 18:36 | 显示全部楼层
爱疯 发表于 2012-11-11 15:31
谢谢群子老师!

群子老师的回复,就像饱和式攻击,特详细、充分,且非常容易查看。

你都已经是超级版主了,为啥还要问这些基础的、简单的问题?

我对VBA的排列/组合,自己做了深入的研究,所以知识比较全面。


但是对于一般爱好者来说,能够用for……next循环写成可执行的代码就足够了。

没必要研究那么细的。

因为里面主要是各种利用VBA数组的算法问题了。一般人即使看了代码,也不容易弄明白的。



回复

使用道具 举报

发表于 2012-11-11 18:43 | 显示全部楼层
上清宫主 发表于 2012-11-10 00:05
对,就是这个意思
从m个元素中取n个(n

呵呵。VBA数组for……next循环的代码例子,我在这个帖子里提供了。

你有兴趣研究一下吧。


简单思路是,对于m个元素中取n个的组合解:
1。 建立储存1-n状态的数组
2。 从末位(n)位开始递增升位,并记录该组合
3。 当末位(n)状态递增到=m时,检查并找到x位置使得在x位进行递增而不导致错误……(详细规则略)
4。 加入终止循环的判断

呵呵。

回复

使用道具 举报

 楼主| 发表于 2012-11-11 21:55 | 显示全部楼层
香川群子 发表于 2012-11-11 18:36
你都已经是超级版主了,为啥还要问这些基础的、简单的问题?

我对VBA的排列/组合,自己做了深入的研究 ...

谢谢群子老师!

下去再慢慢理解了{:25:}
回复

使用道具 举报

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

本版积分规则

小黑屋|手机版|Archiver|Excel精英培训 ( 豫ICP备11015029号 )

GMT+8, 2024-4-29 20:41 , Processed in 0.268726 second(s), 8 queries , Gzip On, Yac On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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