Excel - 生成每行三组数字的笛卡尔积

Excel - Generating the Cartesian product of three sets of numbers in each row

我的单行数据格式如下:

第 1 组(仅包含一个数字)|第 2 组(在独特的单元格中包含 1-6 个数字)|第 3 组(包含 1-6 个独特的单元格|

示例:[1] | [1] [2] [3] | [1] [5]

输出:1 1 1、1 1 5、1 2 1、1 2 5、1 3 1、1 3 5

这里有一个 VBA 函数可以处理 3 个数字集的特殊情况:

Function CartesianProduct(nums1 As Range, nums2 As Range, nums3 As Range) As Variant
    Dim n As Long 'number of products
    Dim i As Long, j As Long, k As Long, r As Long
    Dim products As Variant
    
    n = nums1.Cells.Count * nums2.Cells.Count * nums3.Cells.Count
    ReDim products(1 To n, 1 To 3)
    For i = 1 To nums1.Cells.Count
        For j = 1 To nums2.Cells.Count
            For k = 1 To nums3.Cells.Count
                r = r + 1 'current row
                products(r, 1) = nums1.Cells(i)
                products(r, 2) = nums2.Cells(j)
                products(r, 3) = nums3.Cells(k)
            Next k
        Next j
    Next i
    CartesianProduct = products
End Function

这可以从另一个 VBA 函数或子函数调用,或者直接用作 sheet 中的数组公式:

在上面的截图中我选择了范围A3:C8(需要提前确定它的大小)输入公式

=CartesianProduct(A1,B1:D1,E1:F1)

然后通过输入 Ctrl+Shift+Enter.

接受它作为数组公式

一旦你超过三组,事情就会变得有点棘手,因为你不能在循环方法的必要级别上硬连线,而是可能会使用递归方法,类似于这个答案:

这是一个函数,可以对任意数量的维度进行笛卡尔积 - 每个维度的值必须垂直列出,一个维度可能有多个列(参见下面的示例):

Function CartesianProduct(ParamArray range() As Variant) As Variant
    Dim n As Long 'number of products
    Dim total_dimensions As Long, i As Long, num_dim As Long, num_col As Long, max_cols As Long
    Dim dim_sizes As Variant
    Dim dim_counters As Variant
    Dim products As Variant
    
    ReDim dim_sizes(LBound(range) To UBound(range))
    ReDim dim_counters(LBound(range) To UBound(range))
    n = 1
    max_cols = 0
    For i = LBound(range) To UBound(range)
        dim_sizes(i) = range(i).Rows.Count
        max_cols = max_cols + range(i).Columns.Count
        n = n * dim_sizes(i)
        dim_counters(i) = 1
    Next
    ReDim products(1 To n, 1 To max_cols)
    For i = 1 To n
        carry_one = True
        num_col = max_cols
        For num_dim = UBound(range) To LBound(range) Step -1
            For j = range(num_dim).Columns.Count To 1 Step -1
                products(i, num_col) = range(num_dim).Cells(dim_counters(num_dim), j)
                num_col = num_col - 1
            Next j
            If carry_one = True Then
                dim_counters(num_dim) = dim_counters(num_dim) + 1
                If dim_counters(num_dim) > dim_sizes(num_dim) Then
                    dim_counters(num_dim) = 1
                    carry_one = True
                Else
                    carry_one = False
                End If
            End If
        Next num_dim
    Next i
    CartesianProduct = products
End Function

示例(注意第一个维度有两列):