题目描述
给定一个整数数组,统计其中素数(质数)的个数。
素数定义:在一个大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数。
核心思路
1. 暴力判定法
对于每一个数字 n,我们从 2 开始尝试除到 n-1。如果期间能被整除,说明不是素数。
2. 优化逻辑:$\sqrt{n}$ 法
其实我们不需要一直除到 n-1。
- 如果 $n = a \times b$,那么 $a$ 和 $b$ 中至少有一个会小于或等于 $\sqrt{n}$。
- 例如判断 16 是否为素数,我们只需要检查 2、3、4。因为如果 4 以后还有因子(如 8),那么必然对应一个小于 4 的因子(如 2),而我们在检查到 2 时就已经发现了。
- 结论:只需要遍历到
sqrt(n)即可将单次判定的复杂度从 $O(n)$ 降低到 $O(\sqrt{n})$。
代码实现
1 | /** |
复杂度分析
- 时间复杂度:$O(N \cdot \sqrt{M})$。
- 其中 $N$ 为数组长度,$M$ 为数组中数值的大小。
- 对于数组中的每个元素,我们执行一次判定,判定耗时 $O(\sqrt{M})$。
- 空间复杂度:$O(1)$。只使用了常数个变量进行计数和迭代。
进阶思考:如果要统计 0 到 N 之间的所有素数?
对于大规模范围内的素数统计(例如统计 100 万以内的素数个数),暴力算法会显得力不从心。此时可以采用 埃拉托斯特尼筛法(Sieve of Eratosthenes):
- 建立一个布尔数组
isPrime,初始全部设为true。 - 从 2 开始,如果是素数,则将其所有的倍数(2x, 3x…)标记为
false。 - 这样每个合数只会被标记几次,时间复杂度可以大幅优化至 $O(N \log \log N)$。