【问题标题】:Combination of N variables in VBAVBA中N个变量的组合
【发布时间】:2016-09-21 12:06:55
【问题描述】:

我在 Excel 中遇到了一些复杂的 VBA 组合问题。 我有 n 个由起始值、停止值和步骤定义的变量。 N 个变量是 double 值,基本上如下所示。

      Var1, Var2,  Var3, .... VarN
start:  20,    1,   0.1, ....  
stop : 100,   10,   1.0, ....
step :  10,    1,   0.1, ....

生成每个变量组合的最有效(快速)方式是什么,如下所示:

20, 1, 0.1
20, 1, 0.2
20, 1, 0.3
...
...
...

100, 10, 0.8
100, 10, 0.9
100, 10, 1.0

我相信这种情况下共有 900 种组合 (=9x10x10)。

更具挑战性的部分是我需要更灵活的解决方案来使用像这样的双数组(不涉及任何工作表公式)的 N 变量案例:

Private startValue() As Double ' start value of each variable
Private stopValue() As Double  ' stop value of each variable
Private stepValue() As Double  ' step value of each variable
Private combination() as double 'combination of all N variable
Private n as integer ' number of variable = N


ReDim startValue(1 To 1, 1 To n) As Double
ReDim stopValue(1 To 1, 1 To n) As Double
ReDim stepValue(1 To 1, 1 To n) As Double

非常感谢您的帮助。

亲切的问候。

【问题讨论】:

    标签: excel vba combinations permutation


    【解决方案1】:

    如果您需要 3 个用于 startValue、stopValue 和 stepValue 的数组,为什么您需要 2 维数组。或者,您只能使用一个二维数组

    ReDim arrValue(0 To 2, 0 To n-1) As Double
    

    其中索引 0 = 开始,1 = 停止,2 = 步骤

    【讨论】:

    • 谢谢。在vba中能否高效完成N变量case?
    【解决方案2】:

    您可以让Excel 通过为变量分配一个范围(即arrMyValues = Range("A2:C4"))来完成这项工作,而不是创建一个数组然后为其分配值。但是在你的情况下,我们不知道最后一列,我们首先必须得到最后一列。我们可以通过以下方式获得:

    oWS.Cells(1, Columns.Count).End(xlToLeft).Column
    

    这将返回工作表中使用的列数。然而,在这种情况下,我们需要最后一列的名称。为此,我们首先获取最后一列地址:

    oWS.Cells(1, Columns.Count).End(xlToLeft).Address
    

    然后我们把这个地址拆分成一个数组,这样我们就可以得到列名:

    arrColAddress = Split(oWS.Cells(1, Columns.Count).End(xlToLeft).Address, "$")
    

    现在我们有了最后一列的名称,我们可以使用带有行数的名称将范围传递给我们的变量。要获取工作表的行数,我们可以使用:

    oWS.Cells(Rows.Count, "B").End(xlUp).Row
    

    如果我们将所有这些放在一起(我只添加了一个工作表对象),我们会得到:

    Dim oWS as Worksheet : Set oWS = Worksheets("your worksheet name")
    arrColAddress = Split(oWS.Cells(1, Columns.Count).End(xlToLeft).Address, "$")
    arrMyValues = oWS.Range("A2:" & arrColAddress(1) & oWS.Cells(Rows.Count, "B").End(xlUp).Row)
    

    代码基于您提供的表格

    【讨论】:

    • arrColAddress = Split(oWS.Cells(1, Columns.Count).End(xlToLeft).Address, "$")' will give you the address for the last column in your sheet, which is then Split` 成一个数组。下一行arrMyValues = oWS.Range("A2:" & arrColAddress(1) & oWS.Cells(Rows.Count, "B").End(xlUp).Row)': to explain: if you assign a Range` 到一个变量,Excel 将为您创建数组。这一行给出了要在数组中分配的范围。您关于 N 变量的问题我假设您正在寻找工作表中使用的最后一列。您可以通过以下方式获取此信息:Cells(1, Columns.Count).End(xlToLeft).Column
    • 我已经为代码添加了解释。希望这会有所帮助
    • 嗨。扎克。感谢您的代码。这段代码是否可以像我的示例中那样生成 3 个变量的 900 个组合。此外,这种方法可以适用于 N 变量情况吗?感谢您的帮助。
    • 所有代码所做的就是将工作表中的范围放入一个多维数组中。 N 个变量是指工作表中的 N 列吗?如果是这样,那么是的,代码会这样做。也不确定你是如何安排 3 的组合的。所以如果你有 5 列,你将如何生成 3 的数字组合?
    【解决方案3】:

    这是一个适用于任意数量变量的解决方案:

    'The following takes a 1-based variant area with 3 rows
    'And n columns, where n is the number of variables
    'The first row are start values, the second is stop, and the
    'third is step-size.
    'It returns a variant array consisting of all combos
    
    Function MakeCombos(Vals As Variant) As Variant
        Dim i As Long, j As Long, n As Long
        Dim numCombos As Long
        Dim combos As Variant, levels As Variant
        Dim var As Double, varStep As Double, colStep As Long
    
        If TypeName(Vals) = "Range" Then Vals = Vals.Value 'make into a VBA array if passed a range
        n = UBound(Vals, 2)
        ReDim levels(1 To n)
    
        'first find the *number* of levels for each variable
        numCombos = 1
        For i = 1 To n
            levels(i) = 1 + Round((Vals(2, i) - Vals(1, i)) / Vals(3, i))
            numCombos = numCombos * levels(i)
        Next i
    
        ReDim combos(1 To numCombos, 1 To n)
    
        'Now -- just fill in column by column in reverse order with some modular arithmetic
        colStep = 1 'how often value is changed in column
        For j = n To 1 Step -1
            var = Vals(1, j)
            varStep = Vals(3, j)
            combos(1, j) = var
            For i = 1 To numCombos - 1
                combos(i + 1, j) = var + (Int(i / colStep) Mod levels(j)) * varStep
            Next i
            'before next pass theough outer loop, increase colStep so that
            'in the next column will step more slowly
            colStep = colStep * levels(j)
        Next j
    
        MakeCombos = combos
    
    End Function
    

    为了测试它,我从一个如下所示的电子表格开始:

    然后运行这段代码:

    Sub test()
        Range("F1:H900").Value = MakeCombos(Range("B2:D4"))
    End Sub
    

    之后 F:H 列中的数据以:

    900 行以下结束:

    【讨论】:

    • 这太神奇了。约翰。非常感谢。它准确而灵活。很好的解决方案。
    • @auto9817 很高兴我能帮上忙。如果答案解决了您的问题,您可以将其标记为“已接受”。
    • 接受按钮在哪里?我无法在我的网络浏览器中看到它们。对不起,非常幼稚的问题。 :)
    • @auto9817 Stack Overflow 界面比较杂乱(因为发生了很多事情)。也许这会有所帮助:stackoverflow.com/help/accepted-answer
    猜你喜欢
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 2013-11-19
    • 2011-11-22
    相关资源
    最近更新 更多