【问题标题】:VBA Nested For Loop EfficiencyVBA 嵌套循环效率
【发布时间】:2014-07-15 16:15:50
【问题描述】:

我正在尝试找到在 VBA 中执行任务的最快方法。目前我把它写成一个嵌套的 for 循环,它可能非常慢。我正在遍历一个唯一数字列表并将它们与不同列表中的数字相匹配。如果我得到一个匹配,我将信息存储在一个多维数组中,因为可能有多个匹配,我想跟踪所有这些。不幸的是,这意味着当使用 for 循环时,如果只有 1000 个唯一数字和 5000 个要查找匹配的数字,我的循环最终可能会迭代 1000*5000 = 5000000 次。如您所见,这会很快产生问题。我在问是否有更好的方法来解决这个问题而留在 VBA 中。我已经完成了所有技巧,例如将 screenUpdating 设置为 false 并将计算设置为 manaul。

这是我的代码:

For x = 0 To UBound(arrUniqueNumbers)
    Dim arrInfo() As Variant
    ReDim Preserve arrInfo(0)
    If UBound(arrInfo) = 0 Then
        arrInfo(0) = CStr(arrUniqueNumbers(x))
    End If

    For y = 2 To Length
        UniqueString = CStr(arrUniquePhoneNumbers(x))
        CLEARString = CStr(Sheets(2).Range("E" & y).Value)
        If UniqueString = CLEARString Then 'match!
            NormalizedDate = Format(CStr(Sheets(2).Range("G" & y).Value), "yyyymmdd")
            z = z + 1
            ReDim Preserve arrInfo(z)
            arrInfo(z) = NormalizedDate & " " & LTrim(CStr(Sheets(2).Range("D" & y).Value))
            arrInfo(z) = LTrim(arrInfo(z))
        End If
    Next

    arrUniqueNumbers(x) = arrInfo()
    ReDim arrInfo(0)  'erase everything in arrOwners
    z = 0
Next

【问题讨论】:

  • 也许使用Scripting.Dictionary 更快,因为您可能会在代码的Redim Preserve 部分花费大量时间。
  • 另外,可能想看看 Collection 对象。

标签: arrays performance vba excel


【解决方案1】:

循环效率很低,所以有很多可以避免的瓶颈(主要是按照从最简单到最复杂的顺序)

  1. UniqueString 步骤移出最内层循环:这一步不会随着y 的变化而改变,所以重复它没有意义。
  2. Redim Preserve 移出最内层循环: 您在最内层循环中重新分配内存,效率极低。在循环外分配“足够”的内存。
  3. 不要一直使用Sheets().Range() 访问单元格内容: 每次访问电子表格上的某些内容时,都会造成巨大的拖累,并且与访问相关的开销很大。考虑从电子表格中进行一步提取操作,以及将结果一步推送操作返回电子表格。请参阅下面的示例代码。

电子表格的高效获取和回推操作的示例代码:

Dim VarInput() As Variant
Dim Rng As Range

' Set Rng = whatever range you are looking at, say A1:A1000

VarInput = Rng
' This makes VarInput a 1 x 1000 array where VarInput(1,1) refers to the value in cell A1, etc.
' This is a ONE STEP fetch operation

' Your code goes here, loops and all

Dim OutputVar() as Variant
Redim OutputVar(1 to 1000, 1 to 1)

' Fill values in OutputVar(1,1), (1,2) etc. the way you would like in your output range

Dim OutputRng as Range
Set OutputRng = ActiveSheet.Range("B1:B1000")
' where you want your results

OutputRng = OutputVar
' ONE STEP push operation - pushes all the contents of the variant array onto the spreadsheet

还有很多其他步骤可以进一步显着加快您的代码速度,但这些步骤应该会产生明显的影响,而无需付出太多努力。

【讨论】:

    【解决方案2】:
    dim dict as Object
    set dict = CreateObject("Scripting.Dictionary")
    dim x as Long
    'Fill with ids
    'Are arrUniqueNumbers and arrUniquePhoneNumbers the same?
    For x = 0 To UBound(arrUniqueNumbers)
        dict.add CStr(arrUniquePhoneNumbers(x)), New Collection
    next
    
    'Load Range contents in 2-Dimensional Array
    dim idArray as Variant
    idArray = Sheets(2).Cells(2,"E").resize(Length-2+1).Value
    dim timeArray as Variant
    timeArray = Sheets(2).Cells(2,"G").resize(Length-2+1).Value
    dim somethingArray as Variant
    somethingArray = Sheets(2).Cells(2,"D").resize(Length-2+1).Value
    
    dim y as Long
    'Add Values to Dictionary
    For y = 2 To Length
        Dim CLEARString As String
        CLEARString = CStr(timeArray(y,1))
        If dict.exists(CLEARString) then
            dict(CLEARString).Add LTrim( Format(timeArray(y,1)), "yyyymmdd")) _
                                    & " " & LTrim(CStr(somethingArray(y,1)))
        end if
    next
    

    这样访问

    dim currentId as Variant
    for each currentId in dict.Keys
        dim currentValue as variant
        for each currentValue in dict(currentId)
            debug.Print currentId, currentValue
        next
    next
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多