来自Leetcode第204题计数质数
统计所有小于非负整数 n 的质数的数量。
示例:
1 | 输入: 10 |
筛选
首先从 2 开始,我们知道 2 是一个素数,那么 2 × 2 = 4, 3 × 2 = 6, 4 × 2 = 8… 都不可能是素数了。
然后我们发现 3 也是素数,那么 3 × 2 = 6, 3 × 3 = 9, 3 × 4 = 12… 也都不可能是素数了。
看到这里,你是否有点明白这个排除法的逻辑了呢?先看我们的第一版代码:
1 | int countPrimes(int n) { |
首先,回想刚才判断一个数是否是素数的 isPrime
函数,由于因子的对称性,其中的 for 循环只需要遍历 [2,sqrt(n)]
就够了。这里也是类似的,我们外层的 for 循环也只需要遍历到 sqrt(n)
:
1 | for (int i = 2; i * i < n; i++) |
除此之外,很难注意到内层的 for 循环也可以优化。我们之前的做法是:
1 | for (int j = 2 * i; j < n; j += i) |
这样可以把 i
的整数倍都标记为 false
,但是仍然存在计算冗余。
比如 n = 25
,i = 4
时算法会标记 4 × 2 = 8,4 × 3 = 12 等等数字,但是这两个数字已经被 i = 2
和 i = 3
的 2 × 4 和 3 × 4 标记了。
我们可以稍微优化一下,让 j
从 i
的平方开始遍历,而不是从 2 * i
开始:
1 | for (int j = i * i; j < n; j += i) |
这样,素数计数的算法就高效实现了,其实这个算法有一个名字,叫做 Sieve of Eratosthenes。看下完整的最终代码:
1 | int countPrimes(int n) { |