我正在努力了解Eratosthenes" Sieve of Eratosthenes"。这是我的算法(下面的代码),以及我无法理解的功能列表(按顺序)。
i * i
比i * 2
效率更高?是的,我可以理解它会减少迭代次数,因此效率更高,但是它不会跳过一些数字(例如i = 9
=>
j = 81
skips 18 27 36 ...
) ?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;
}
答案 0 :(得分:6)
为什么我*我比i * 2更有效率?是的,我可以理解这将是更少的迭代,因此效率更高,但是它不会跳过一些数字(例如i = 9 =&gt; 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的倍数在我们最终达到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)
的一个子集。
答案 1 :(得分:3)
为什么我*我比i * 2更有效率?它没有跳过一些数字吗?
不,它不是因为i
的较小倍数(例如,在i = 2
,i = 3
等运行循环时,您的案例中包括18,27等等)
每个数字都可以表示为唯一的素数因子分解。如果i
是素数,则i
大于i
且小于i * i
的任意倍数将是小于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)位操作。