1
社区成员
发帖
与我相关
我的任务
分享在使用埃氏筛选法求质数中,遇到一些不太好理解的地方。
完整代码链接如下:
主要代码片段如下:
for (int i = 2; i * i <= n; i++) { // 从2开始遍历到根号n
if (isPrime[i]) { // 如果当前数是素数
for (int j = i * i; j <= n; j += i) { // 将当前素数的倍数标记为非素数
isPrime[j] = false;
}
}
}
以下是个人的一些理解:
1. 首先对于 for (int i = 2; i * i <= n; i++)中的 i * i <= n 的理解:
这里使用了优化技巧,因为如果一个数n不是素数,那么它的最小质因数肯定小于等于根号n。所以只需要遍历到根号n即可。
2. 在 for (int j = i * i; j <= n; j += i)中 int j = i * i 的作用是什么?
int j = i * i 的作用是从 i 的平方开始标记非素数。
但又为什么是从平方开始?
这是因为在 i 的平方之前的 i 的倍数已经被之前的素数标记过了,所以我们只需要从 i 的平方开始标记即可,避免重复标记。
举个例子来说明:
- 当 i = 2 时,我们从 4 开始标记非素数,因为 2 的倍数 4 已经被标记为非素数了。
- 当 i = 3 时,我们从 9 开始标记非素数,因为 3 的倍数 6 和 9 已经被之前的素数(2)标记为非素数了。
这样做的好处是可以避免重复标记,提高算法的效率。因此, int j = i*i 是为了确保我们从 i 的平方开始标记非素数,而不是从 i 的倍数开始,避免重复标记。
3. 对于for (int j = i * i; j <= n; j += i) 中为什么是 j = j + i ,而不是 j = j * i。
将当前的倍数标记为非素数,更新条件表达式应该是使用乘法表示倍数吧。
但是请看下面示例:
以2的倍数为例
加法:
2+2=4
2+2+2=6
2+2+2+2=8
2+2+2+2+2=10
乘法:
2*2=4
2*2*2=8
2*2*2*2=16
2*2*2*2*2=32
如果我们使用 j = j * i 来递增,那么 j 的增长速度会过快, 则会跳过某些 j 的倍数,导致未能正确标记所有的非素数。
对此乘法的本质可以理解为重复加法的过程。
这样就可以理解: 2的倍数是不断的加2,而不是一味的乘2。
或者是 2*2, 2*3,2*4 ...
所以以上代码中还是使用加法比较合适,如果使用乘法的话,需要再嵌套个循环吧?
这里没有去验证,感觉需要花费一点时间,等闲的没事再去验证吧。
所以以上的代码片段是进行优化过的。
未优化的代码片段如下:
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
for (int j = i + i; j <= n; j += i) {
isPrime[j] = false;
}
}
}