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对话框中:

  1. 一个友好的名字为您的连接

  2. 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