Excel VBA创build一个范围的每个可能的组合
我有一个问题,我无法在网上find任何地方(它可能在那里,但我找不到它,嘿)。
我有一个包含13列数据的电子表格。 每一列都包含需要进入整个testing用例的参数变化。
他们都不一样,像
E:
101%
105%
110%
120%
记者:
上部S
上行L
下行B
优质V
我已经看到了使用嵌套循环的组合问题的几个解决scheme。 我想避开13个嵌套循环(但这是我现在最好的select)。 对于如何在每一列中生成每一个独特的组合,我感到茫然。
我不确定这对你们是否有意义。 我希望有人能用recursionalgorithm至less指向正确的方向。 我想使它足够dynamic,以采取不同数量的列和行。
谢谢你们可以给我的任何帮助。
由于我提供了一种ODBC方法,所以我想我应该详细说明,因为如何做到这一点并不明显。 而且,诚实地说,我需要重新学习这个过程并为自己logging。
这是一种使用Excel和Microsoft Query生成两个或多个一维数据数组的笛卡尔积的方法。
这些说明是用XL2007编写的,但是应该在任何版本中使用较小的(如果有的话)修改。
步骤1
按列组织数组。
重要提示:每列应有两个“标题”名称,如下所示。 最高的名字将被解释为“表名”。 第二个名字将被解释为“列名”。 这将在几步后显而易见。
依次select每个数据范围,包括两个“标题”,然后Ctrl+Shift+F3
。 仅在“创build名称”对话框中打勾,然后单击“ OK
。
一旦所有命名范围build立,保存文件。
第2步
数据| 获取外部数据| 来自其他来源| 从Microsoft Query
select<New Data Source>
。 在“ Choose New Data Source
对话框中:
-
一个友好的名字为您的连接
-
select适当的Microsoft Excel驱动程序
…然后Connect
第3步
Select Workbook...
然后浏览您的文件。
步骤4
添加你的“表”中的“列”。 现在您可以看到为什么步骤1中的“两个标题”布局很重要 – 它让驾驶员正确理解数据。
接下来点击Cancel
(真的!)。 此时可能会提示您“继续在Microsoft Query中编辑?” (回答Yes
),或者在graphics编辑器中无法表示连接的投诉。 忽略这个并且伪造…
第5步
Microsoft Query将打开,默认情况下,您添加的表将被交叉连接。 这将产生一个笛卡尔产品,这是我们想要的。
现在完全closuresMSQuery。
第6步
您将返回到工作表。 差不多完成了,我保证! 勾选New worksheet
然后OK
。
第七步
交叉结果被返回。
不知道为什么你不愿意循环。 看到这个例子。 花了不到一秒钟。
Option Explicit Sub Sample() Dim i As Long, j As Long, k As Long, l As Long Dim CountComb As Long, lastrow As Long Range("G2").Value = Now Application.ScreenUpdating = False CountComb = 0: lastrow = 6 For i = 1 To 4: For j = 1 To 4 For k = 1 To 8: For l = 1 To 12 Range("G" & lastrow).Value = Range("A" & i).Value & "/" & _ Range("B" & j).Value & "/" & _ Range("C" & k).Value & "/" & _ Range("D" & l).Value lastrow = lastrow + 1 CountComb = CountComb + 1 Next: Next Next: Next Range("G1").Value = CountComb Range("G3").Value = Now Application.ScreenUpdating = True End Sub
快照
注 :以上是一个小例子。 我做了四列200行的testing。 在这种情况下可能的总组合是1600000000
,花了16秒。
在这种情况下,它跨越Excel行限制。 我能想到的另一个select是在这种情况下将输出写入文本文件。 如果你的数据很小,那么你可以不用数组而直接写入单元格。 :)但在大数据的情况下,我会build议使用数组。
解决scheme基于我的第二个评论。 这个例子假设你有三列数据,但可以适应更多的处理。
我从你的样本数据开始。 为了方便起见,我在第一行添加了数字。 我还添加了组合的总数(计数的产品)。 这是Sheet1
:
在Sheet2
:
公式:
A2:C2
(橙色单元)硬编码=0
A3=IF(SUM(B3:C3)=0,MOD(A2+1,Sheet1!$E$1),A2) B3=IF(C3=0,MOD(B2+1,Sheet1!$G$1),B2) C3=MOD(C2+1,Sheet1!$J$1) D2=INDEX(Sheet1!$E$2:$E$5,Sheet2!A2+1) E2=INDEX(Sheet1!$G$2:$G$6,Sheet2!B2+1) F2=INDEX(Sheet1!$J$2:$J$5,Sheet2!C2+1)
从第3行向下填充与Sheet1
显示的Total
相同的行
我自己多次需要这个,并最终build立它。
我相信代码可以对任何列的总数和任意数量的不同值进行缩放(例如,每列可以包含任意数量的值)
它假定每列中的所有值都是唯一的(如果不是这样,则会得到重复的行)
它假设你想交叉连接输出基于你当前select的任何单元格(确保你全部选中)
它假定您希望输出在当前select之后开始一列。
它是如何工作的(简单地说):首先为每一列和每一行:它计算支持所有组合在N列中所需的总行数(列1中的项目*列2中的项目*列中的项目*
每列第二:基于总的组合,以及以前列的总组合计算两个循环。
ValueCycles(您必须循环显示当前列中所有值的次数)ValueRepeats(连续重复列中每个值的次数)
Sub sub_CrossJoin() Dim rg_Selection As Range Dim rg_Col As Range Dim rg_Row As Range Dim rg_Cell As Range Dim rg_DestinationCol As Range Dim rg_DestinationCell As Range Dim int_PriorCombos As Long Dim int_TotalCombos As Long Dim int_ValueRowCount As Long Dim int_ValueRepeats As Long Dim int_ValueRepeater As Long Dim int_ValueCycles As Long Dim int_ValueCycler As Long int_TotalCombos = 1 int_PriorCombos = 1 int_ValueRowCount = 0 int_ValueCycler = 0 int_ValueRepeater = 0 Set rg_Selection = Selection Set rg_DestinationCol = rg_Selection.Cells(1, 1) Set rg_DestinationCol = rg_DestinationCol.Offset(0, rg_Selection.Columns.Count) 'get total combos For Each rg_Col In rg_Selection.Columns int_ValueRowCount = 0 For Each rg_Row In rg_Col.Cells If rg_Row.Value = "" Then Exit For End If int_ValueRowCount = int_ValueRowCount + 1 Next rg_Row int_TotalCombos = int_TotalCombos * int_ValueRowCount Next rg_Col int_ValueRowCount = 0 'for each column, calculate the repeats needed for each row value and then populate the destination For Each rg_Col In rg_Selection.Columns int_ValueRowCount = 0 For Each rg_Row In rg_Col.Cells If rg_Row.Value = "" Then Exit For End If int_ValueRowCount = int_ValueRowCount + 1 Next rg_Row int_PriorCombos = int_PriorCombos * int_ValueRowCount int_ValueRepeats = int_TotalCombos / int_PriorCombos int_ValueCycles = (int_TotalCombos / int_ValueRepeats) / int_ValueRowCount int_ValueCycler = 0 int_ValueRepeater = 0 Set rg_DestinationCell = rg_DestinationCol For int_ValueCycler = 1 To int_ValueCycles For Each rg_Row In rg_Col.Cells If rg_Row.Value = "" Then Exit For End If For int_ValueRepeater = 1 To int_ValueRepeats rg_DestinationCell.Value = rg_Row.Value Set rg_DestinationCell = rg_DestinationCell.Offset(1, 0) Next int_ValueRepeater Next rg_Row Next int_ValueCycler Set rg_DestinationCol = rg_DestinationCol.Offset(0, 1) Next rg_Col End Sub
调用该方法并将其放入当前级别,该方法将在方法中递减(对于eng而言为抱歉)
样品:
sub MyAdd(i as integer) if i > 1 then MyAdd = i + MyAdd(i-1) else MyAdd = 1 end if end sub