11

我有一个问题,我无法在网络上的任何地方找到(它可能在那里,但我找不到,呵呵)。

我有一个包含 13 列数据的电子表格。每一列都包含需要进入整个测试用例的参数的变体。

它们都不同,比如

E:
101%
105%
110%
120%

J:
Upper S
Upside L
Downside B
Premium V

我已经看到了几种使用嵌套循环的组合问题的解决方案。我想避开 13 个嵌套循环(但这是我目前最好的选择)。我对如何在每列中生成每个唯一组合感到不知所措。

我不确定这对你们是否足够有意义。我希望有人至少可以用递归算法为我指明正确的方向。我想让它足够动态以获取不同数量的列和行。

感谢你们能给我的任何帮助。

4

5 回答 5

22

由于我提供了一种 ODBC 方法,因此我认为我应该详细说明它,因为如何做到这一点并不是很明显。而且,老实说,我需要重新学习这个过程并为自己记录下来。

这是一种使用 Excel 和 Microsoft Query 生成两个或多个一维数据数组的笛卡尔积的方法。

这些说明是用 XL2007 编写的,但应该在任何版本中进行微小的(如果有的话)修改。

第1步

按列组织数组。

重要提示:每一列都应该有两个“标题”名称,如下面的粗体所示。最上面的名称稍后将被解释为“表名”。第二个名称将被解释为“列名”。这将在几步之后变得明显。

依次选择每个数据范围,包括两个“标题”,然后点击Ctrl+Shift+F3。仅Top row在“创建名称”对话框中打勾并单击OK

建立所有命名范围后,保存文件。

在此处输入图像描述

第2步

数据 | 获取外部数据 | 从其他来源 | 来自微软查询

选择<New Data Source>。在Choose New Data Source对话框中:

  1. 连接的友好名称

  2. 选择适当的 Microsoft Excel 驱动程序

... 然后Connect

在此处输入图像描述

第 3 步

Select Workbook...然后浏览您的文件。

在此处输入图像描述

第4步

从“表格”中添加“列”。您现在可以看到为什么第 1 步中的“双标头”布局很重要——它诱使驱动程序正确理解数据。

下一步单击Cancel(真的!)。此时可能会提示您“继续在 Microsoft Query 中编辑?” (答案Yes),或加入的投诉无法在图形编辑器中表示。忽略这一点,继续前进……

在此处输入图像描述

第 5 步

Microsoft Query 将打开,默认情况下,您添加的表将被交叉连接。这将生成一个笛卡尔积,这正是我们想要的。

现在完全关闭 MSQuery。

在此处输入图像描述

第 6 步

您将返回到工作表。快完成了,我保证!勾选New worksheetOK

在此处输入图像描述

第 7 步

返回交叉连接的结果。

在此处输入图像描述

于 2012-05-22T00:53:47.500 回答
10

不知道你为什么反对循环。请参阅此示例。花了不到一秒钟的时间。

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

快照

在此处输入图像描述

注意:以上是一个小例子。我对 4 列进行了测试,每列有 200 行。在这种情况下可能的总组合是160000000016 秒。

在这种情况下,它会超过 Excel 行数限制。我能想到的另一种选择是在这种情况下将输出写入文本文件。如果您的数据很小,那么您可以在不使用数组并直接写入单元格的情况下摆脱困境。:) 但是在大数据的情况下,我建议使用数组。

于 2012-05-21T23:10:34.163 回答
5

我自己多次需要这个并最终构建了它。

我相信代码适用于任何列总数和列内任何数量的不同值(例如,每列可以包含任意数量的值)

它假设每列中的所有值都是唯一的(如果这不是真的,你会得到重复的行)

它假定您要根据当前选择的任何单元格交叉连接输出(确保全部选择)

它假定您希望输出在当前选择之后开始一列。

它是如何工作的(简要):首先对于每一列和每一行:它计算支持 N 列中所有组合所需的总行数(第 1 列中的项目 * 第 2 列中的项目 ... * 第 N 列中的项目)

每列第二个:根据总组合数和前一列的总组合数,它计算两个循环。

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
于 2012-11-14T18:39:41.097 回答
3

基于我的第二条评论的解决方案。此示例假设您有三列数据,但可以调整以处理更多数据。

我从你的样本数据开始。为了方便起见,我在第一行添加了计数。我还添加了组合的总数(计数的乘积)。这是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)

Total从第 3 行向下填充与显示的一样多的行Sheet1

于 2012-05-21T22:20:07.027 回答
0

调用方法并放入当前级别,将在方法中递减(抱歉eng)

样本:

    sub MyAdd(i as integer)
      if i > 1 then
        MyAdd = i + MyAdd(i-1)
      else
        MyAdd = 1
      end if
    end sub
于 2012-05-21T21:42:14.503 回答