优化嵌套循环的算法

时间:2011-09-17 21:22:03

标签: algorithm for-loop

是否有可用于优化以下性能的算法?

for (i = 0; i < LIMIT; i++) {
  for (j = 0; j < LIMIT; j++) {
   // do something with i and j
  }
 }
  • ij都从0
  • 开始
  • 两个循环以相同的条件结束
  • ij都以相同的速率递增

这可以以某种方式在一个循环中完成吗?

6 个答案:

答案 0 :(得分:12)

可以使用一个循环来写这个,但我强烈建议不要这样做。双循环是一个成熟的习惯,程序员知道如何阅读,如果你将两个循环折叠成一个,你就牺牲了可读性。此外,目前还不清楚这是否会使代码运行得更快,因为编译器已经非常擅长优化循环。将两个循环折叠成一个循环需要在每个步骤进行一些额外的数学运算,这几乎肯定比两个循环独立慢。

那就是说,如果你想把它写成一个循环,一个想法是考虑迭代空间,你迭代的对的集合。现在,看起来像这样:

(0, 0)   (0, 1),   (0, 2), ...,   (0, N-1)
(1, 0)   (1, 1),   (1, 2), ...,   (1, N-1)
                ...          
(N-1, 0) (N-1, 1), (N-1, 2), ..., (N-1, N-1)

我们的想法是尝试按(0, 0), (0, 1), ..., (0, N-1), (1, 0), (1, 1), ..., (1, N-1), ..., (N-1, 0), (N-1, 1), ..., (N-1, N-1)的顺序访问所有这些对。要做到这一点,请注意,每次我们增加i时,我们都会跳过N元素,而当我们增加j时,我们会跳过一个元素。因此,循环的迭代(i, j)将映射到线性化循环排序中的位置i * N + j。这意味着在迭代i * N + j上,我们想要访问(i, j)。为此,我们可以使用一些简单的算法从索引中恢复ij。如果k是当前循环计数器,我们想访问

i = k / N   (integer division)
j = k % N

因此循环可以写成

for (int k = 0; k < N * N; ++k) {
    int i = k / N;
    int j = k % N;
}

但是,您必须小心这一点,因为N * N可能不适合整数,因此可能会溢出。在这种情况下,你会想要回到双重for循环。此外,额外的除法和模数的引入将使这个代码运行(可能)比双for循环慢得多。最后,这段代码比原始代码更难阅读,你需要确保提供积极的评论来描述你在这里做的事情。同样,我强烈建议你不要这样做,除非你有充分的理由怀疑标准双for循环有问题。

(有趣的是,这里使用的技巧也可以用来表示使用一维数组的多维数组。逻辑是相同的 - 你有一个想要用一维结构表示的二维结构。 )

希望这有帮助!

答案 1 :(得分:4)

没有办法显着优化循环本身。但是,当你考虑&#34;对i和j&#34;做一些事情的细节时,无论i还是j是外循环,它都会产生很大的不同。例如,一个订单可能导致内存或磁盘中的大量跳转,而另一个订单导致顺序访问,或几乎如此。

此外,您可以通过移动不依赖内部索引从内部循环到外部循环的计算来优化双循环,也可以使用临时变量。智能编译器可以将其优化到一定程度,但它们并不完美。

答案 2 :(得分:1)

你无法改善循环的大O性能。但是,有一种依赖于算法的方法,通过利用缓存来改善big-O隐藏的常数因子。

以下是改进的矩阵转置算法的示例:A Cache Efficient Matrix Transpose Program?

然而,这里的共同主题是我们实际上引入了更多循环,而不是更少。

答案 3 :(得分:1)

如果你必须以任何价格加速for循环,看看你是否可以找到并行化或矢量化编译器并根据需要进行修改以使其利用它,或者找到一种方法来使用某些构建块库。参见例如http://en.wikipedia.org/wiki/Intel_C%2B%2B_Compilerhttp://en.wikipedia.org/wiki/Math_Kernel_Library

(或者找一个更好的算法 - 通常会给你类似的东西:

for (i = 0; i < LIMIT; i++) {
  // Do something clever with i 
  // that does not depend on j
  for (j = 0; j < LIMIT; j++) {
    // do something fast with i and j
    // and the results of the clever stuff
    // outside the loop over j
  }
}

答案 4 :(得分:0)

这取决于你是否需要内循环中的i和j,例如有时你可以压扁这样的循环:

for (k = 0; k < LIMIT * LIMIT; ++k)
{
    // do something with k
}

但是对于除了最琐碎的内部循环之外的所有内容,它可能对性能没有任何可察觉的差异。

您实际上试图解决哪些具体问题?

答案 5 :(得分:-1)

我前一段时间遇到过同样的问题......

您如何看待这个?单个while循环(i是示例中的forter for循环的索引):

i = 0; j = 0;
while (i<M) {
  // Do something with i and j
  if (j<N-1) {
    j++;
  } else {
    j=0;
    i++;
  }
}