204.计算质数
质数在之前的篇章中我们也进行过一个初步的了解,质数的定义是:只包含1和本身为因子的数就叫做质数,质数又称为素数。例如2,3,5,7,11等,他们的因子都只有两个,那就是1和他们本身。
今天这道题我们是要计算出小于n的质数有多少个,大多数人第一想法就是遍历,当然这也是最直接,最方便的解法,思维方式也不算复杂,只需在区间[2,√n]中查询是否还有能够整除n的数,有则返回true,否则返回false。
代码如下:
bool isPrime(int n) {
int i, sqrtn = sqrt(n + 1e-6);
if(n <= 1) {
return false;
}
for(i = 2; i <= sqrtn; ++i) {
if(n % i == 0) {
return false;
}
}
return true;
}
int countPrimes(int n){
int count = 0;
for(int i = 2; i < n; i++){
bool b = isPrime(i);
if(b){
count++;
}
}
return count;
}
这种解法的时间复杂度是O(n√n),但这道题的数据范围是[0,5000001],如果用这种解法的话,计算时间还是有点大,会超出时间限制,所以接下来我们介绍另外一种解法——Eratosthenes法。
我们定义一个数组dp[ ],其中dp[ i ]表示数字 i 是否是质数,如果 i 不是,则dp[ i ] = true。初始条件是dp[ 0 ] = dp[ 1 ] = true。
分析:
- 从未被标记的数中找到最小的数 i,i * 2,i * 3,i * 4 等2的倍数都标记为true,因为2为质数,所以2,4,6,8,10…都被标记为质数。
- 在从下一个最小未被标记的数3计算,6,9,12,15…标记为质数。
- 以此类推。
多举几个例子,我们可以发现每个i在计算之前,[1, i)的范围内都被上一个质数 i 给计算过。
如图:
所以我们只需从 [i ^ 2,n)的 区间范围内计算。
代码如下:
#define ll long long
bool dp[5000001];
int countPrimes(int n){
if(n == 0 || n == 1){
return 0;
}
int count = 0;
dp[0] = dp[1] = false;
for(int i = 2; i < n; i++){
if(!dp[i]){
//若为被标记,则说明i为质数,count++
count++;
//对i的i * i倍进行标记
for(ll j = (ll) i * i; j < n; j+=i){
dp[j] = true;
}
}
}
return count;
}