【问题标题】:Efficiency of Sieve of Eratosthenes algorithm埃拉托色尼筛算法的效率
【发布时间】:2018-07-03 19:47:18
【问题描述】:

我正在尝试理解“埃拉托色尼筛法”。这是我的算法(下面的代码),以及我无法理解的功能列表(按顺序)。

  1. 为什么i * ii * 2 更高效?是的,我可以理解它会减少迭代次数,因此效率更高,但它不会跳过一些数字(例如i = 9=>j = 81skips 18 27 36 ...)吗?
  2. 在维基百科上,我发现空间复杂度等于O(n),这是可以理解的;无论我们输入什么数字,它都会创建一个输入大小的数组,但是这里的时间复杂度是让事情变得混乱的地方。我找到了这个符号O(n(logn)(loglogn))——那是什么?据我了解,我们有 2 次完整迭代和 1 次部分迭代,因此 O(n^2 * logn)
#include <iostream>
using namespace std;

int main() {
  cout << "Enter number:" << endl;

  int arrSize;
  cin >> arrSize;

  bool primesArr[arrSize];
  primesArr[0] = false;
  for (int i = 1; i < arrSize; i++) primesArr[i] = true;

  for (int i = 2; i < arrSize; i++)

    if (primesArr[i - 1]) {
      cout << i << endl;
   /* for (int j = i * 2; j < arrSize; j += i) less efficient */
      for (int j = i * i; j < arrSize; j += i)
        primesArr[j - 1] = false;
    }

  return 0;
}

【问题讨论】:

  • VLA 在 C++ 中不是一个好主意。大尺寸 VLA 完全是一个不同的问题。更喜欢容器。
  • 所有小于i的倍数在之前的迭代中已经被消除了。因此,i * i 是尚未消除的i 的最小倍数。 (把1到100的数字写在纸上,用手执行算法看看。)
  • 至于i * i 问题,它不会跳过任何内容,因为每个小于i^2 的数字要么是素数,要么具有小于i 的素数,因此已经被标记在上一步。
  • "根据我的理解,我们有 2 次完整迭代和 1 次部分迭代,因此 O(n^2 * logn)" 复杂度很难计算,尤其是在不平凡的情况下,你怎么想出来与 O(n^2*logn)?

标签: c++ algorithm performance primes sieve-of-eratosthenes


【解决方案1】:

为什么 i * i 比 i * 2 更有效?是的,我可以理解它会减少迭代,从而提高效率,但它不会跳过一些数字(例如 i = 9 => j = 81 skip 18 27 36 ...)吗?

你指的是

for (int j = i * i; j < arrSize; j += i)

注意i * i 是循环计数器j初始 值。所以j大于i * i的值都会被标记掉。我们从i * 2 跳过到i * i 的值在之前的迭代中已经被标记了。让我们想想前几个:

i == 2 时,我们标记出所有 2 的倍数(2、4、6、8 等)。当i == 3时,如果我们开始j = 3 * 2 = 6,那么我们将在到达9、12、15等之前再次标记6。由于6是2的倍数并且已经被标记,我们可以直接跳到3 * 3 == 9

当我们到达i == 5,如果我们从j == 5 * 2 == 10开始,那么我们将标记10,因为它是2的倍数,15是3的倍数,20是已经处理好的。在我们最终达到 25 之前也是 2 的倍数,这不是任何小于 5 的引物的倍数。

这里的时间复杂度是事情变得混乱的地方。我发现了这个符号 O(n(logn)(loglogn)) - 那是什么?根据我的理解,我们有 2 次完整迭代和 1 次部分迭代,因此 O(n^2 * logn)。

您的分析得出正确结果,该算法为O(n^2 * logn)A more detailed analysis 可以证明更严格的上限为O(n(logn)(loglogn))。请注意,O(n(logn)(loglogn))O(n^2 * logn) 的子集。

【讨论】:

    【解决方案2】:

    为什么 i * i 比 i * 2 更有效?它不会跳过一些数字吗?

    不,不是因为i 的倍数较小(例如,在为i = 2i = 3 等运行循环时,会覆盖您的情况下的 18、27 等)

    每个数字都可以表示为唯一的素数分解。如果i 是一个素数,任何大于i 且小于i * ii 的倍数都将是一个或多个小于i 的素数的倍数。

    讨厌的符号O(n(logn)(loglogn))

    来自this answer
    操作数为 1/2 + 1/3 + 1/5 + 1/7 ... = n log log n
    如果您计算位操作,因为您处理的数字最多为 n,所以它们大约有 log n 位,这是 log n 的因数,给出 O(n log n log log n) 位操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-16
      • 2013-03-20
      相关资源
      最近更新 更多