|
本帖最后由 香川群子 于 2012-10-4 19:40 编辑
- Dim sj, jg(), m%, k, h '定义公用变量
- Sub kagawa_3() '递归组合求和-3 【倒序递归剪枝+末位计算提速】
- tms = Timer
- h = [b1] '获取目标和值
- m = [a1].End(4).Row: ' sj0 = [a1].Resize(m) '获取原始数据个数m(A列最大行数)
- [a1].Resize(m).Sort [a1], 1, , , 2 '如果原始数据乱序则先升序排序
- sj = [a1].Resize(m, 2): ' [a1].Resize(m) = sj0 '排序处理后原始数据恢复原状
-
- sj(1, 2) = sj(1, 1)
- For i = 2 To m
- sj(i, 2) = sj(i - 1, 2) + sj(i, 1)
- Next
- '以上为计算升序时的累计和值,以便剪枝时使用
- ReDim jg(65536, 0) '定义输出结果的数组jg
- k = 0 '符合条件个数k初始化
-
- ' Open "D:\Documents\Result.txt" For Output As #1
- ' Print #1, m & " / " & h
- '直接输出结果到记事本文件时打开记事本文件的代码,如果不用则注释掉
- Call dgH3(0, "", m + 1) '调用递归过程代码,注意到 i 参数为m+1开始
-
- ' Print #1, "Result: " & k & "/ Calc " & cnt & "/ Time: " & Format(Timer - tms, "0.000s")
- ' Close #1
- '直接输出结果到记事本文件时关闭记事本文件用的代码,如果不用则注释掉
-
- If k > 0 And k < 65536 Then [f:f] = "": [f1].Resize(k) = jg '输出结果数组jg中的值到F列
- MsgBox "Result: " & k & "/ Calc " & cnt & " Time: " & Format(Timer - tms, "0.000s")
- End Sub
- Sub dgH3(r, s$, i%) '递归过程 r=和值累计、s=文本计算式结果、i=递归位置参数(对应第几个元素)
- Dim j%
-
- x = h - r '【目标和值h】-【当前和值r】的【差值x】
- For j = 1 To i - 1 '只能用正序法检查1~i-1的元素范围中的值sj(j,1)正好等于差值x
- If x = sj(j, 1) Then
- If k < 65536 Then
- jg(k, 0) = s & "+" & x '按计算顺序正向输出结果
- ' jg(k, 0) = "+" & x & s '按计算顺序反向输出结果
- End If
- k = k + 1
- ' Print #1, s & "+" & x '结果输出到记事本文件时用的输出代码,不用则注释掉
- ' Application.StatusBar = "kagawa: " & k & " / " & s & "+" & x '当前进度显示,调试时可用
- Exit For '找到符合条件的末位值时即可退出,否则要循环到底。
- End If
- Next
- '以上代码,即为末位计算法的实现,提速效果明显(大大降低了无效递归的循环次数。也可以算是一种剪枝)
- For j = j - 1 To 1 Step -1 '从上面计算差值得到相等值的前一位置j-1开始倒序检查
- If x >= sj(j, 1) Then 'If Not r + sj(j, 1) > h Then '如果本位置值sj(j,1)加上累计r值以后还是不大于目标和值h,则可进入递归,否则剪枝退出。
- If x > sj(j, 2) Then 'If r + sj(j, 2) < h Then
- Exit For '如果倒序递增到该位置的累计和值sj(j,2)加上累计r值以后还是不足目标和值h时就可提前退出。这也是剪枝提速的关键。
- Else 'If Not r + sj(j, 2) < h Then
- Call dgH3(r + sj(j, 1), s & "+" & sj(j, 1), j) '按计算顺序正向输出结果
- ' Call dgH3(r + sj(j, 1), "+" & sj(j, 1) & s, j) '按计算顺序反向输出结果
- End If
- End If
- Next
- End Sub
复制代码 |
|