如何优化这些C for循环?

时间:2013-12-18 20:34:09

标签: c performance for-loop

我选择在C中执行这些Project Euler问题,因为我的印象是C很快,但似乎并非如此。以下两个循环都慢:

int problem_7 () { 
  int n = 0;
  for (int i = 1; i <= 10001; i++) {
      for (int j = (i==1)?1:n+1; j > 0; j++) {
          int factorCounter = 0;
          for (int k = 1; k <= j/2; k++)
              factorCounter += (j % k == 0);
          if (factorCounter == 1) {
              n = j;
              break;
          }
      }
  }
  return n;
}


long long int problem_10 () {
  long long int sum = 0;
  for (int i = 2; i < 2000000; i++) {
      int factorCount = 0;
      for (int j = 1; j <= i/2; j++) {
          factorCount += (i % j == 0);
          sum += i*(j == i/2 && factorCount == 1);
      }
  }

  return sum; 
} 

有什么方法可以让这些循环运行得更快?他们做了他们应该做的事,但他们每人花了5分钟来执行。

4 个答案:

答案 0 :(得分:2)

我想我会回答这个问题。你的程序很慢,因为你的循环设计很差而且嵌套很差。我不打算进行复杂性分析,但是你在O(N ^ x)的范围内,其中x> 4(据我所知)。

这只是糟糕的编程,它与循环优化几乎没有关系。你需要使用像Sieve of Eratosthenes这样的东西来解决Eu​​ler 7.我不会在这里给你解决方案,就像维基文章那样解决它是相当简单的。

答案 1 :(得分:2)

使用布尔数值的分支预测不是必需的:

以问题7为例,这可以通过使用 if语句来加速:

  int n = 0;
  for (int i = 1; i <= 10001; i++) {
      for (int j = (i==1)?1:n+1; j > 0; j++) {
          int factorCounter = 0;
          for (int k = 1; k <= j/2; k++)
          {
              if (j%k==0)                 // code is changed HERE
              {
                factorCounter ++;
                if (factorCounter > 1)
                {
                      break;
                }
              }                          // code change ends here
          }
          if (factorCounter == 1) {
              n = j;
              break;
          }
      }

这完成了 0.88secs 而不是原来的 9.5secs - 比这一次更改快了10倍

优化解释和对等理性:

所做的更改来自原始行:

factorCounter += (j % k == 0);

首先将其改为等效的:

if (j%k==0)
{
    factorCounter ++;
}

注意factorCounter只能递增,并且在循环之后,任何超过1的值都会被丢弃,因为(factorCounter == 1)将为false,所以一旦它大于1,就没有理由继续循环。另请注意factorCounter的值只能在(j%k==0)时更改,因此{if}}的测试应该在if检查中发生,代码现在是:

factorCounter > 1

早期退出循环就是性能增益

答案 2 :(得分:1)

C很快。更高级别的语言也可以更快。但即使是最好的编译器也无法优化你所做的额外操作!如果factorCounter变得大于1,减少问题7算法中操作次数的一种简单方法就是打破最深的循环。抱歉打破它但是:它不是编译器它是算法!

答案 3 :(得分:1)

几乎可以立即执行更直接的问题。你真的不需要那么多的优化。

#include <iostream>
#include <cmath>

using namespace std;

bool isPrime(int j)
{
    for(int fac = 2; fac < sqrt(j)+0.5; ++fac)
        if(j%fac == 0)
            return false;

    return true;
}

int main()
{
    int primesFound = 1; // 2 is prime
    int possiblePrime = 1;

    while(primesFound != 10001)
    {
        possiblePrime += 2;
        if(isPrime(possiblePrime))
            ++primesFound;
    }

    cout << possiblePrime << endl;
}