|
递归用得好。
用字典去重复会更快一些。- Public sj, d, jg(), m, n, k, jg2(), h, cnt
- Sub kagawa()
- Set d = CreateObject("scripting.dictionary")
- tms = Timer
- m = [a1].End(4).Row - 1: n = [b5]: h = [b2] '获取元素个数m,组合个数n,目标总和h
- sj0 = [a2].Resize(m): [a2].Resize(m).Sort [a2], 1, , , 2 '原始数据,按从小到大排序
- sj = [a2].Resize(m) '排序后数据读入原始数据数组sj
- [a2].Resize(m) = sj0 '恢复原始数据的排序状态
- If n > m Then AC = 65536 Else AC = WorksheetFunction.Combin(m, n)
- If AC > 65535 Then ReDim jg(65535, n) Else ReDim jg(AC, n) '计算并定义结果数组jg
- k = 1: cnt = 0 '结果序数k归零,全部计算次数cnt归零
- ReDim jg2(65536, 0) '定义输出所有计算过程明细的结果数组jg2
- Call bcfhdg("", 0, 0, 0) '调用递归过程代码
- '以下为输出结果部分的代码,解释从略
- jg(0, 0) = "Summary": For i = 1 To n: jg(0, i) = "n" & i: Next
- [e1].CurrentRegion = "": If k > 1 Then [e1].Resize(k, n + 1) = jg
- 'If cnt > 1 Then If cnt > 65535 Then [d1].Resize(65536) = jg2 Else [d1].Resize(cnt) = jg2
- [d1] = "<= " & h & " Detail: " & cnt - 1
- [e1].Resize(, n + 2).EntireColumn.AutoFit
- MsgBox "Calc " & cnt - 1 & " ,Get " & k - 1 & " result." & vbCr & Format(Timer - tms, "0.000s")
- End Sub
- Sub bcfhdg(s, r, i, t%) '递归过程代码
- ' If cnt < 65536 Then jg2(cnt, 0) = s '"=" & Mid(s, 2) '如果不想看详细组合过程,把这句注释掉速度加快
- ' cnt = cnt + 1
- If r = h Then '如果计算总和结果r 符合目标总和值h
- If n > m Or t = n Then '当n>m时输出结果,或当n为小于等于m的指定值且当前个数t=n时
- If Not d.exists(s) Then
- d.Add s, Nothing
- p = Split(s, "+")
- For j = 1 To UBound(p)
- jg(k, j) = p(j) '组合结果写入数组jg
- Next
- jg(k, 0) = "=" & Mid(s, 2) '第1列写入完整计算式
- k = k + 1 '结果序号递增+1
- End If
- End If
- Exit Sub '结束递归(因为以后再加一个数的话肯定会超过目标值h)
- End If
- If t = n Then Exit Sub '如果n>m即不指定组合个数时继续,否则停止
- For j = i + 1 To m
- If r + sj(j, 1) > h Then Exit For '如果下一个加总结果r>目标值h,则可推出循环。
- Call bcfhdg(s & "+" & sj(j, 1), r + sj(j, 1), j, t + 1) '继续调用递归代码,保证递归继续
- Next j
- End Sub
复制代码 |
|