【问题标题】:What is the difference between the time complexity of these two ways of using loops in VBA?这两种在 VBA 中使用循环的方式的时间复杂度有什么区别?
【发布时间】:2011-06-17 05:03:39
【问题描述】:

我有一个理论问题,如果你在这里给我建议,将不胜感激。

说,我们有这两段代码。 第一个:

For Each cell In rng1
    collectionOfValues.Add (cell.Value)
Next

For Each cell In rng2
   collectionOfAddresses.Add (cell.Address)
Next

For i = 1 To collectionOfAddresses.Count
   Range(collectionOfAddresses.Item(i)) = collectionOfValues.Item(i)
Next i

在这里,我们将一个范围的地址添加到某个集合,并将另一个范围的值添加到第二个集合,然后用这些值填充这些地址上的单元格。

这是第二个代码,它是一样的:

For i = 1 To rng1.Rows.Count
  For j = 1 To rng1.Columns.Count
       rng2.Cells(i, j) = rng1.Cells(i, j)
  Next j
Next i

所以,问题是 - 这两种情况下的执行时间是多少?我的意思是,很明显第二种情况是 O(n^2) (为了更容易,我们假设范围是正方形)。

第一个呢? For Each 是否被视为嵌套循环?

如果是这样,是否意味着第一个代码的时间是 O(n^2) + O(n^2) + O(n^2) = 3*O(n^2)和第二个code time一样吗?

一般来说,这两个代码除了第一个在创建集合时需要额外的内存之外,是否有所不同?

非常感谢。

【问题讨论】:

  • 您可以考虑编辑此内容并用问题重新命名。比如,“这两种在 VBA 中使用循环的方式的时间复杂度有区别吗?”...

标签: vba excel time-complexity big-o


【解决方案1】:

其实你的第一个例子是O(n^4)!

这听起来可能令人惊讶,但这是因为对 VBA 集合的索引具有线性而非恒定的复杂性。 VBA 集合本质上具有列表的性能特征 - 按索引 获取元素 N 所需的时间与 N 成正比。按索引 迭代整个事物所需的时间成正比到 N^2。 (我在你身上切换了案例来区分 N,数据结构中的元素数量,从你的 n,一个方形单元格一侧的单元格数量。所以这里 N = n^2。)

这就是 VBA 使用 For...Each 符号来迭代集合的原因之一。当您使用 For...Each 时,VBA 在幕后使用迭代器,因此遍历整个 Collection 是 O(N) 而不是 O(N^2)。

所以,切换回你的 n,你的前两个循环使用 For...Each 在一个具有 n^2 单元格的范围内,所以它们都是 O(n^2)。您的第三个循环是在具有 n^2 个元素的 Collection 上使用 For...Next,即 O(n^4)。

我实际上并不确定您的最后一个循环,因为我不知道 Range 的 Cells 属性是如何工作的——那里可能存在一些额外的隐藏复杂性。但我认为 Cells 将具有数组的性能特征,因此 O(1) 用于按索引进行随机访问,这将使最后一个循环 O(n^2)。

这是 Joel Spolsky 所说的“画家施莱米尔算法”的一个很好的例子:

一定有画家的施莱米尔 算法在某处。每当 似乎应该有的东西 线性性能,但似乎 有n平方的表现,寻找 隐藏的施莱米尔。他们经常 被您的图书馆隐藏。

(在stackoverflow成立之前看到这篇文章:http://www.joelonsoftware.com/articles/fog0000000319.html)

有关 VBA 性能的更多信息,请访问 Doug Jenkins 的网站:

http://newtonexcelbach.wordpress.com/2010/03/07/the-speed-of-loops/

http://newtonexcelbach.wordpress.com/2010/01/15/good-practice-best-practice-or-just-practice/

(如果这是一个“真正的”程序而不仅仅是一个学习练习,我也会支持cyberkiwi所说的不要循环遍历范围来复制单元格内容。)

【讨论】:

【解决方案2】:

切记不要将代码的复杂性与后台 Excel 函数的复杂性混为一谈。在这两种情况下,完成的所有工作量都是 N^2。但是,在您的第一个示例中-您的代码实际上只有 3N (三个循环中的每一个都为 N)。 Excel 中的一条语句可以填写多个值这一事实并不会改变您编写的代码的复杂性。 foreach 循环与 for 循环相同 - N 复杂度本身。嵌套循环时只会得到 N^2。

要回答您关于哪个更好的问题 - 通常最好尽可能使用内置函数。假设应该是内部 Excel 将比您自己编写的运行效率更高。但是(了解 MS) - 如果性能是优先事项,请确保始终检查该假设。

【讨论】:

  • 我不认为这是正确的,但我愿意被告知我为什么错了。看我的回答。
【解决方案3】:

你是对的,第一个是 3 x O(n^2),但请记住,O-notation 不关心常量,所以就复杂性而言,它仍然是 O(n^2) algorithm。

第一个不被视为嵌套循环,即使它与第二个循环的大小相同。这只是对 Excel 中 N 项范围的直接迭代。使它成为 N^2 的原因是您将 N 定义为边的长度,即行数/列数(正方形)。

只是一个 Excel VBA 注释,您不应该循环遍历单元格或存储地址。这两种方法都不是最优的。但我认为它们有助于说明您理解 O 表示法的问题。

rng1.Copy
rng2.Cells(1).PasteSpecial xlValues
Application.CutCopyMode = False

【讨论】:

  • 我不认为这是正确的,但我愿意被告知我为什么错了。看我的回答。
  • 更好:rng2.Value = rng1.Value
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-13
  • 2020-01-12
相关资源
最近更新 更多