【问题标题】:Combination Algorithm in Excel VBAExcel VBA中的组合算法
【发布时间】:2011-11-04 02:49:53
【问题描述】:

我需要一种算法,它可以生成一组数字的所有可能组合,并将它们全部输出到 Excel 电子表格中。

例如,使用 n = 5(1,2,3,4,5) 和 r = 2(为此创建了一个小 gui),它将生成所有可能的组合并将它们输出到这样的 excel 电子表格中。 .

1,2
1,3
1,4
...

打印的顺序无关紧要。它可以先打印 (5,1),然后打印 (1,2)。 谁能告诉我如何做到这一点?

非常感谢。

【问题讨论】:

  • 订单重要吗? 5,1 和 1,5 一样吗?
  • 如果顺序(正如 Tim 所问的)很重要,那么“所有可能的组合”可以快速增长。如果 n 和 r 均为 8,则为阶乘 8,或超过 40,000 个排列。你对 n 有限制吗?
  • 是的,顺序很重要。很抱歉没有把它放进去。1,5 和 5,1 一样。
  • 不,我对 n 或 r 没有限制。我想让它动态化,这样任何用户都可以输入任何数字,它会生成包含所有可能组合的电子表格。
  • 不敢相信还没有人问过这个问题:你自己尝试过什么吗?答案基本上是两个嵌套的For Next 循环。

标签: vba excel combinations


【解决方案1】:

这段代码怎么样...

Option Explicit

Private c As Integer

Sub test_print_nCr()
  print_nCr 5, 3, Range("A1")
End Sub

Function print_nCr(n As Integer, r As Integer, p As Range)
  c = 1
  internal_print_nCr n, r, p, 1, 1
End Function


Private Function internal_print_nCr(n As Integer, r As Integer, ByVal p As Range, Optional i As Integer, Optional l As Integer) As Integer

  ' n is the number of items we are choosing from
  ' r is the number of items to choose
  ' p is the upper corner of the output range
  ' i is the minimum item we are allowed to pick
  ' l is how many levels we are in to the choosing
  ' c is the complete set we are working on

  If n < 1 Or r > n Or r < 0 Then Err.Raise 1
  If i < 1 Then i = 1
  If l < 1 Then l = 1
  If c < 1 Then c = 1
  If r = 0 then 
    p = 1
    Exit Function
  End If

  Dim x As Integer
  Dim y As Integer

  For x = i To n - r + 1
    If r = 1 Then
      If c > 1 Then
        For y = 0 To l - 2
          If p.Offset(c - 1, y) = "" Then p.Offset(c - 1, y) = p.Offset(c - 2, y)
        Next
      End If
      p.Offset(c - 1, l - 1) = x
      c = c + 1
    Else
      p.Offset(c - 1, l - 1) = x
      internal_print_nCr n, r - 1, p, x + 1, l + 1
    End If
  Next

End Function

【讨论】:

  • 谢谢!现在我只需要弄清楚如何在每个单元格上打印它们。我是 VBA 新手,所以过去 2 天我一直在学习它。
【解决方案2】:

我不得不这样做一次,最终适应了this algorithm。它与嵌套循环有些不同,因此您可能会觉得它很有趣。翻译成VB,会是这样的:

Public Sub printCombinations(ByRef pool() As Integer, ByVal r As Integer)
    Dim n As Integer
    n = UBound(pool) - LBound(pool) + 1

   ' Please do add error handling for when r>n

    Dim idx() As Integer
    ReDim idx(1 To r)
    For i = 1 To r
        idx(i) = i
    Next i

    Do
        'Write current combination
        For j = 1 To r
            Debug.Print pool(idx(j));
            'or whatever you want to do with the numbers
        Next j
        Debug.Print

        ' Locate last non-max index
        i = r
        While (idx(i) = n - r + i)
            i = i - 1
            If i = 0 Then
                'All indexes have reached their max, so we're done
                Exit Sub
            End If
        Wend

        'Increase it and populate the following indexes accordingly
        idx(i) = idx(i) + 1
        For j = i + 1 To r
            idx(j) = idx(i) + j - i
        Next j
    Loop
End Sub

【讨论】:

  • 谢谢。我试过了,它工作正常,但 Excel 电子表格中的数组输入不是我想要的。但我试过了,它非常适合任何可能需要它的人。
  • 这就是我说“类似”的原因:-)
  • @Joubarc 它是否适用于这样的事情:array(2,3,4,5,7,10),where r=3?
【解决方案3】:

这些组合算法最好使用带有递归的嵌套循环。大约 4 年前,我编写了执行此操作所需的代码 (https://vitoshacademy.com/vba-nested-loops-with-recursion)。这个想法是改变Main中的size变量和同一个Sub中的输入数组。然后运行它:

Sub Main()

    Static size         As Long
    Static c            As Variant
    Static arr          As Variant
    Static n            As Long

    size = 2
    c = Array(1, 2, 3, 4, 5, 6)

    n = UBound(c) + 1
    ReDim arr(size - 1)

    EmbeddedLoops 0, size, c, n, arr

End Sub

Function EmbeddedLoops(index, k, c, n, arr)

    Dim i                   As Variant

    If index >= k Then
        PrintArrayOnSingleLine arr
    Else
        For Each i In c
            arr(index) = i
            EmbeddedLoops index + 1, k, c, n, arr
        Next i
    End If

End Function

debug.print 在 VBA 中具有内置限制,在即时窗口中仅显示最后 200 个值 (Ctrl+G)。因此,如果您有超过 200 行的结果,最好写入 Excel 电子表格、txt.file 或数据库:

Public Sub PrintArrayOnSingleLine(myArray As Variant)

    Dim counter     As Integer
    Dim textArray     As String

    For counter = LBound(myArray) To UBound(myArray)
        textArray = textArray & myArray(counter)
    Next counter

    Debug.Print textArray

End Sub

【讨论】:

    【解决方案4】:

    这是我使用数组 vba 的解决方案

    Private Sub UserForm_Initialize()
    
    Dim matriz_origen() As Variant
    
    Dim matriz_destino() As Variant
    
    Dim n As Long
    
    Dim k As Long
    
    n = 6
    
    k = 2
    
    Call combinatoria(matriz_origen, matriz_destino, n, k)
    
    'Def titulo
    
    Title = "Matriz Combinatoria"
    
    'FUnction Calling
    
    Call despliegue_2D(matriz_destino, Style, Title)
    
    End Sub
    
    
    Function combinatoria(matriz() As Variant, comb As Long, _
                          matriz_origen() As Variant, matriz_destino() As Variant, _
                          n As Long, k As Long)
    
    'This function is calculating all possible combinations.
    
    comb = Application.WorksheetFunction.Combin(n, k) 'Sin repeticion
    
    ReDim matriz_origen(1 To n, 1 To k)
    
    'Loops
    
    For j = 1 To k
    
         For i = 1 To n
    
             matriz_origen(i, j) = i
    
         Next i
    
    Next j
    
    ReDim matriz_destino(1 To comb, 1 To k) 'comb
    
    If (k = 2) Then
    
    cont1 = 1
    
    'Loops
    
    For j = 1 To k - 1
    
    pos1 = j + 1
    
    For i = 1 To n
    
    For iter1 = 1 To n
    
     If matriz_origen(i, j) < matriz_origen(iter1, pos1) Then
    
        matriz_destino(cont1, j) = matriz_origen(i, j)
    
        matriz_destino(cont1, pos1) = matriz_origen(iter1, j)
    
        cont1 = cont1 + 1
    
     End If
    
    Next iter1
    
    Next i
    
    Next j
    
    End If
    
    If (k = 3) Then
    
    cont1 = 1
    
    'Loops
    
    For j = 1 To k - 2
    
    pos1 = j + 1
    
    pos2 = j + 2
    
    For i = 1 To n
    
    For iter1 = 1 To n
    
     If matriz_origen(i, j) < matriz_origen(iter1, pos1) Then
    
      For iter2 = 1 To n
    
       If matriz_origen(iter1, pos1) < matriz_origen(iter2, pos2) Then
    
        matriz_destino(cont1, j) = matriz_origen(i, j)
    
        matriz_destino(cont1, pos1) = matriz_origen(iter1, j)
    
        matriz_destino(cont1, pos2) = matriz_origen(iter2, j)
    
        cont1 = cont1 + 1
    
       End If
    
     Next iter2
    
     End If
    
    Next iter1
    
    Next i
    
    Next j
    
    End If
    
    If (k = 4) Then
    
    cont1 = 1
    
    'Loops
    
    For j = 1 To k - 3
    
    pos1 = j + 1
    
    pos2 = j + 2
    
    pos3 = j + 3
    
    For i = 1 To n
    
    For iter1 = 1 To n
    
     If matriz_origen(i, j) < matriz_origen(iter1, pos1) Then
    
      For iter2 = 1 To n
    
      If matriz_origen(iter1, pos1) < matriz_origen(iter2, pos2) Then
    
        For iter3 = 1 To n
    
          If matriz_origen(iter2, j) < matriz_origen(iter3, pos1) Then
    
           matriz_destino(cont1, j) = matriz_origen(i, j)
    
           matriz_destino(cont1, pos1) = matriz_origen(iter1, j)
    
           matriz_destino(cont1, pos2) = matriz_origen(iter2, j)
    
           matriz_destino(cont1, pos3) = matriz_origen(iter3, j)
    
           cont1 = cont1 + 1
    
           End If
    
        Next iter3
    
       End If
    
     Next iter2
    
     End If
    
    Next iter1
    
    Next i
    
    Next j
    
    End If
    
    End Function
    
    
    Function despliegue_2D(matriz() As Variant, Style As String, Title As String)
    
    'Esta funcion permite el despliegue de un arreglo multidimentinal de 2 dimensiones.
    
    'Declaration
    
    Dim msg As String
    
    Dim iter1 As Integer, iter2 As Integer
    
    'Declaration
    
    filas = UBound(matriz, 1)
    
    columnas = UBound(matriz, 2)
    
    'Loops
    
    For iter1 = 1 To filas
    
        For iter2 = 1 To columnas
    
            msg = msg & matriz(iter1, iter2) & vbTab
    
        Next iter2
    
        msg = msg & vbCrLf
    
    Next iter1
    
    Response = MsgBox(msg, Style, Title)
    
    End Function
    

    【讨论】:

    • 最后加入数组
    • 欢迎来到 SO!当您发布回复时,请尝试检查它是否是原始问题的答案。在您的情况下,如果是,我们无法打结,因为您的代码的一部分正在调用未公开为 despliegue_1D 的过程。您正在修复项目的数量,这是可变的...编辑您的回复并完成您的代码。
    • 感谢您的 cmets,我将使用附加函数和矩阵来完成我的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    相关资源
    最近更新 更多