【问题标题】:C++ - Sieve of Atkin code optimizationsC++ - 阿特金筛法代码优化
【发布时间】:2015-04-16 05:32:56
【问题描述】:

我想知道是否有人对我可以做些什么来提高代码的运行速度有任何建议。我写了一个Sieve of Atkin 函数,它返回一个向量,包括来自[2, max] 的所有素数。

这是我的代码。

void atkin_sieve (unsigned int max, std::vector <unsigned int> & primes) {
  // Sieve array up to max defaulted to false
  // Index's [0, max] correspond to numbers
  // Dynamic memory so all values default false
  bool* sieve = new bool [max + 1];
  // Square root of max number
  unsigned int sqrt_max = int (sqrt (max));
  // Unsigned integers declared to save time
  unsigned int n, x, y;

  // TODO - Explain this
  for (x = 1; x < sqrt_max; x++) {
    for (y = 1; y < sqrt_max; y++) {
      n = (4 * x * x) + (y * y);
      if (n <= max && (n % 12 == 1 || n % 12 == 5))
    sieve [n] = !sieve [n];
      n = (3 * x * x) + (y * y);
      if (n <= max && (n % 12 == 7))
    sieve [n] = !sieve [n];
      n = (3 * x * x) - (y * y);
      if (x > y && n <= max && (n % 12 == 11))
    sieve [n] = !sieve [n];
    }
  }

  // TODO - Explain this
  for (x = 5; x < sqrt_max; x++) {
    if (sieve [x]) {
      n = x * x;
      for (y = n; y <= max; y += n)
    sieve [y] = false;
    }
  }

  // Push primes 2, 3, and 5
  primes.push_back(2);
  primes.push_back(3);
  primes.push_back(5);
  // Start from prime 7, skip even numbers
  for (x = 7; x <= max; x += 2) {
    // If the number is prime
    if (sieve [x])
      // Push it into the vector
      primes.push_back(x);
  }

  // Delete the sieve array
  delete [] sieve;
}

我有几个问题想知道我可以做些什么来优化这个功能。

我将筛子数组初始化为布尔值的动态数组,以便它们都默认为 false,让筛子像这样动态是更快还是应该将其保留为普通数组?

在算法处理完毕后,我使用 for 循环将素数存储在向量中,有没有更快的方法可以找到筛子中的所有素数以将它们存储在向量中?

欢迎提供任何其他提示、技巧、提示或代码,非常感谢,谢谢。

【问题讨论】:

  • 这不应该是在代码审查中吗?
  • bool* sieve = new bool [max + 1];自找麻烦,改用std::unique_ptr&lt;bool[]&gt; sieve(new bool [max + 1]);

标签: c++ optimization sieve-of-atkin


【解决方案1】:

根据您返回的素数数量,使用std::vector 可能会导致问题,每次向量必须增长以处理超过其容量的对象时,它可能需要创建一个新数组并复制所有值从旧阵列到新阵列。考虑使用std::liststd::deque 来避免此问题。如果你真的需要vector,它可能会更快地循环并计算素数的数量,然后reserve 向量中有那么大的大小,因此向量永远不需要增长。

您可能应该对代码进行一些分析 - 您如何执行此操作取决于您的开发环境。这应该告诉您代码的大部分时间都花在了哪里。如果没有这些信息,您可能会花费很长时间来优化代码的一部分,但它不会对结果产生巨大影响。

至少添加一些计时代码,这样您就可以看到您所做的任何更改是否有帮助,以及有多少帮助。

最好的优化通常是改变算法,通常做循环展开之类的事情最好留给编译器,除非代码对时间特别敏感(即使这样也可能不是)。

另外,请确保您在编译时启用了优化 - 这会产生巨大的影响。

【讨论】:

    【解决方案2】:

    正如另一个答案所建议的那样,分析是确定时间去向的最佳方式。

    一些建议和cmets,其中一些可能是显而易见的

    • 我不相信您实际上可以保证您的筛子分配为零初始化;为此,您需要使用bool *sieve = new bool[max+1]();。如果您发现一切正常,那么您要么在这里很幸运,要么在零初始化调试构建的编译器下运行。如果是这种情况,请尝试启用优化的发布版本,您会发现速度有所提高。

    • 您的 bool[] 很可能不会用完每个元素 1 位,但可能会用完一个字节。您可能会发现使用std::vector&lt;bool&gt; 更有效,因为它通常专门用于密集存储布尔值 - 每个布尔值 1 位。您将在减少内存占用与增加读取/写入单个布尔值的复杂性之间进行权衡。

    • 尝试通过适当的输入验证将素数数组的大小预置为 max / log(max),作为小于最大值的素数的近似值。

    • 如果在您的应用程序中重复调用该函数,请尝试重用之前的筛子数组来响应后续调用。

    【讨论】:

      猜你喜欢
      • 2010-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-15
      • 1970-01-01
      • 2014-05-23
      • 1970-01-01
      • 2012-11-13
      相关资源
      最近更新 更多