【问题标题】:Is there a more efficient way to calculate the power set of an array?有没有更有效的方法来计算数组的幂集?
【发布时间】:2017-07-13 15:02:58
【问题描述】:

这是我当前使用位的实现:

Function Array_PowerSet(Self)
    Array_PowerSet = Array()
    PowerSetUpperBound = -1
    For Combination = 1 To 2 ^ (UBound(Self) - LBound(Self)) ' I don't want the null set
        Subset = Array()
        SubsetUpperBound = -1
        For NthBit = 0 To Int(WorksheetFunction.Log(Combination, 2))
            If Combination And 2 ^ NthBit Then
                SubsetUpperBound = SubsetUpperBound + 1
                ReDim Preserve Self(0 To SubsetUpperBound)
                Subset(SubsetUpperBound) = Self(NthBit)
            End If
        Next
        PowerSetUpperBound = PowerSetUpperBound + 1
        ReDim Preserve Array_PowerSet(0 To PowerSetUpperBound)
        Array_PowerSet(PowerSetUpperBound) = Subset
    Next
End Function

请忽略对变体的滥用。 Array_PushArray_Size 应该是不言自明的。

以前,我为每个组合生成一个二进制字符串,但这涉及调用另一个效率不高的函数。

除了使用较少的变体和在内部移动外部函数调用之外,有什么方法可以提高效率吗?

编辑:这是一个完全独立的版本。

Function Array_PowerSet(Self As Variant) As Variant
    Dim PowerSet() As Variant, PowerSetIndex As Long, Size As Long, Combination As Long, NthBit As Long
    PowerSetIndex = -1: Size = UBound(Self) - LBound(Self) + 1
    ReDim PowerSet(0 To 2 ^ Size - 2) ' Don't want null set

    For Combination = 1 To 2 ^ Size - 1
        Dim Subset() As Variant, SubsetIndex As Long: SubsetIndex = -1

        For NthBit = 0 To Int(WorksheetFunction.Log(Combination, 2))
            If Combination And 2 ^ NthBit Then
                SubsetIndex = SubsetIndex + 1
                ReDim Preserve Subset(0 To SubsetIndex)
                Subset(SubsetIndex) = Self(NthBit)
            End If
        Next

        PowerSetIndex = PowerSetIndex + 1
        PowerSet(PowerSetIndex) = Subset
    Next

    Array_PowerSet = PowerSet
End Function

还有一个测试:

Dim Input_() As Variant, Output_() As Variant, Subset As Variant, Value As Variant
Input_ = Array(1, 2, 3)
Output_ = Array_PowerSet(Input_)

For Each Subset In Output_
    Dim StringRep As String: StringRep = "{"

    For Each Value In Subset
        StringRep = StringRep & Value & ", "
    Next

    Debug.Print Left$(StringRep, Len(StringRep) - 2) & "}"
Next

【问题讨论】:

  • 为什么不提供所有相关代码并将其设为minimal reproducible exampleArray_Push 可能是瓶颈(例如,如果它是 ReDim Preserve 添加另一个元素的包装器,那么这是非常低效的,因为您重复复制元素)。
  • Array_Push 和 Array_Size 应该是不言自明的。 - 如果您在编码论坛上寻求帮助,则不是......
  • 更新了帖子。

标签: vba excel set powerset


【解决方案1】:

由于子集的数量呈指数增长,没有算法是真正有效的,尽管您正在做的事情还有改进的空间:

ReDim Preserve,当用于将数组扩展为单个项目时,效率低下,因为它涉及创建一个多出 1 个空间的新数组,然后将旧元素复制到新数组中。最好预先分配足够的空间,然后将其修剪到合适的大小:

Function PowerSet(Items As Variant) As Variant
    'assumes that Items is a 0-based array
    'returns a 0-based jagged array of subsets of Items
    'where each subset is a 0-based array

    Dim PS As Variant
    Dim i As Long, j As Long, k As Long, n As Long
    Dim subset As Variant

    n = 1 + UBound(Items) 'cardinality of the base set
    ReDim PS(0 To 2 ^ n - 2)
    For i = 1 To 2 ^ n - 1
        subset = Array()
        ReDim subset(0 To n - 1)
        k = -1 'will be highest used index of the subset
        For j = 0 To n - 1
            If i And 2 ^ j Then
                k = k + 1
                subset(k) = Items(j)
            End If
        Next j
        ReDim Preserve subset(0 To k)
        PS(i - 1) = subset
    Next i
    PowerSet = PS
End Function

一个测试函数:

Sub test()
    Dim stuff As Variant, subsets As Variant
    Dim i As Long

    stuff = Array("a", "b", "c", "d")
    subsets = PowerSet(stuff)
    For i = LBound(subsets) To UBound(subsets)
        Cells(i + 1, 1).Value = "{" & Join(subsets(i), ",") & "}"
    Next i
End Sub

【讨论】:

  • ArrayLists 会是这个任务的更好选择吗?我可以在返回之前对它们调用 ToArray。
  • @HaoZhang Benchmark 看看。使用 ArrayLists 肯定会更优雅,但是使用外部库会有一定的开销。我的直觉是,它既不会有多大帮助,也不会造成多大伤害。 ArrayList 代码不能移植到 Mac 的 VBA,但对于大多数 Excel VBA 用户来说,这不是问题。
【解决方案2】:

使用集合来构建你的集合是一种选择...

Function Generator()
    Dim Arr() As Variant: Arr = Array(1, 2, 3, 4)
    Dim PSCol As Collection: Set PSCol = PowerSetCol(Arr)
    Dim SubSet As Collection, SubSetStr As String

    For i = 1 To PSCol.Count
        Set SubSet = PSCol.Item(i)
        SubSetStr = "{"
        For j = 1 To SubSet.Count
            SubSetStr = SubSetStr & SubSet.Item(j) & IIf(j = SubSet.Count, "", ", ")
        Next j
        SubSetStr = SubSetStr & "}"
        Debug.Print SubSetStr
    Next i
End Function

Function PowerSetCol(Arr As Variant) As Collection

    Dim n As Long, i As Long
    Dim Temp As New Collection, SubSet As Collection

    For i = 1 To 2 ^ (UBound(Arr) + 1) - 1
        Set SubSet = New Collection
        For n = 0 To UBound(Arr)
            If i And 2 ^ n Then SubSet.Add Arr(n)
        Next n
        Temp.Add SubSet
    Next i
    Set PowerSetCol = Temp
End Function

*********编辑*********

显然,通过索引访问集合比通过项目枚举更密集。还;你不能像@John Coleman 所说的那样直接使用 join ,但是可以使用单行函数来代替它。

希望下面的代码是一个更优化的解决方案

Function Generator()
    Dim Arr() As Variant: Arr = Array(1, 2, 3, 4)
    Dim PSColl As Collection: Set PSColl = PowerSetColl(Arr)

    Dim Str As String, Coll As Collection, Item As Variant
    For Each Coll In PSColl
        Str = ""
        For Each Item In Coll
            Str = strJoin(", ", Str, CStr(Item))
        Next Item
        Debug.Print "{" & Str & "}"
    Next Coll
End Function

Function PowerSetColl(Arr As Variant) As Collection
    Dim Temp As New Collection, SubSet As Collection
    Dim n As Long, i As Long

    For i = 1 To 2 ^ (UBound(Arr) + 1) - 1
        Set SubSet = New Collection
        For n = 0 To UBound(Arr)
            If i And 2 ^ n Then SubSet.Add Arr(n)
        Next n
        Temp.Add SubSet
    Next i
    Set PowerSetColl = Temp
End Function

Function strJoin(Delimiter As String, Optional Str1 As String, Optional Str2 As String) As String
    strJoin = IIf(IsMissing(Str1) Or Str1 = "", Str2, IIf(IsMissing(Str2) Or Str2 = "", Str1, Str1 & Delimiter & Str2))
End Function

【讨论】:

  • 收藏品当然是这个(+1)的自然选择。遗憾的是没有内置的方法将它们转换为数组或直接在它们上使用Join()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-11-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多