在做MATCHfunction如FINDfunction

我正在尝试使MATCH函数像FIND函数一样工作。 首先,我生成虚拟数据用于testing。 这是我使用的例程:

Sub Data_Generator() Randomize Dim Data(1 To 100000, 1 To 1) Dim p As Single For i = 1 To 100000 p = Rnd() If p < 0.4 Then Data(i, 1) = "A" ElseIf p >= 0.4 And p <= 0.7 Then Data(i, 1) = "B" Else Data(i, 1) = "C" End If Next i Range("A1:A100000") = Data End Sub 

现在,我创build一个子例程来查找范围Data的stringA. 这里有两种使用MATCHfunction的方法。 第一种方法是像下面的代码重置查找数组的范围:

 Sub Find_Match_1() T0 = Timer Dim i As Long, j As Long, k As Long, Data As Range Dim Output(1 To 100000, 1 To 1) On Error GoTo Finish Do Set Data = Range(Cells(j + 1, 1), "A100000") 'Reset the range of lookup array i = WorksheetFunction.Match("A", Data, 0) j = j + i Output(j, 1) = j 'Label the position of A k = k + 1 'Counting the number of [A] found Loop Finish: Range("B1:B100000") = Output InputBox "The number of [A] found are " & k & " in", "Process is complete", Timer - T0 End Sub 

对于第二种方法,我通过赋值vbNullString来指定A所在范围的单元格,而不是重置Range("A1:A100000") 。 这个想法是在find之后删除stringA ,并期望MATCH函数在Range("A1:A100000")find下一个stringA. 这是实现第二种方法的代码:

 Sub Find_Match_2() T0 = Timer Dim n As Long, i As Long, j As Long Dim Data_Store() Dim Output(1 To 100000, 1 To 1) Data_Store = Range("A1:A100000") On Error GoTo Finish Do j = WorksheetFunction.Match("A", Range("A1:A100000"), 0) Output(j, 1) = j Cells(j, 1) = vbNullString n = n + 1 Loop Finish: Range("A1:A100000") = Data_Store Range("B1:B100000") = Output InputBox "The number of [A] found are " & n & " in", "Process is complete", Timer - T0 End Sub 

目标是确定哪种方法在性能上采用MATCH函数更好。 原来第一种方法只完成了不到0.4秒,同时第二种方法在我的电脑上完成了大约一分钟。 所以我的问题是:

  1. 为什么第二种方法需要很长时间才能完成?
  2. 如何提高第二种方法的性能?
  3. MATCH函数可以用在数组中吗?

我同意这更多是一个代码审查问题,但我select了自己的好奇心,所以我会分享我发现的。

我认为你正在碰到一个非常经典的N和N ^ 2计算复杂度的情况。 看看你们两个似乎非常相似的方法,并考虑他们实际在做什么,记住当你使用Match_type = 0时,MATCH函数可能只是一个线性search(因为你的数据是未sorting的,而其他匹配types可以对你的sorting数据进行二进制search)。

方法1:

  • 从A1开始
  • 继续往下,直到find“A”
  • 在MATCH下方的单元重新启动

方法2:

  • 从A1开始
  • 继续往下,直到find“A”
  • 清除“A”
  • 在A1重新开始

应该立刻显而易见的是,尽pipe一种方法不断缩小search范围,但另一种方法总是从第一个单元开始search整个范围。 这将解释一些加速问题,并且已经将方法1提升到了一个很好的领先地位,但它甚至不是完整的故事。

真正的关键在于比赛必须为每种情况做的工作量。 因为它的范围不断缩小,并且将其开始顺序移动到列表的下方,无论方法1的匹配从哪个单元开始,它只需要在击中A之前searchless量单元并恢复外部循环。 与此同时,方法2不断地摧毁A,使得它们越来越不密集,迫使自己越来越多地search范围,才得到命中。 最后,方法2循环了近10万个空单元/ B's / C,然后find下一个A.

所以平均来说,方法1的匹配每次只能通过几个单元来查看,而方法2的匹配随着时间的推移需要越来越长的时间,直到结束时它被迫遍历整个范围。 最重要的是,方法2正在对单元格值进行一系列的写操作,比你想象的要慢数万倍。

诚实地说,你最好的select就是一次循环遍历单元格,寻找A和处理它们。 MATCH对表格没有任何优势,方法1基本上只是我描述的循环的更复杂的版本。

我会写这样的东西:

 Sub Find_Match_3() T0 = Timer Dim k As Long, r As Range Dim Output(1 To 100000, 1 To 1) For Each r In Range("A1:A100000").Cells If r.Value = "A" Then Output(r.Row, 1) = r.Row 'Label the position of A k = k + 1 'Counting the number of [A] found End If Next Range("B1:B100000") = Output InputBox "The number of [A] found are " & k & " in", "Process is complete", Timer - T0 End Sub 

这比我的机器快了大约30%。