质数筛:
质数定义:
若一个正整数无法被除了 1 和它自身之外的任何自然数整除,则称该数为质数
(或者素数),否则称该自然数为合数。
题目链接:
int countPrimes(int n){
int sum = 0;
while(n--){
if(isPrime(n))sum++;
}
return sum;
}
题目逻辑很简单,主要是判断素数。下面我们来看isPrime函数的编写。
1.暴力解法:
bool isPrime(int n){
if(n<2){
return false;
}
for(int i = 2; i < sqrt(n); i++){
if(n % i == 0){
return false;
}
}
return true;
}
时间复杂度:emm O(n),算上外面那层while就是O(n^2),很明显过不去,超时了。
那么接下来先优化一下吧。我们知道,一个数是不是质数得由两个数的乘积能否得到它来判断,所以,我们可以发现,i < n 这个判断条件,完全可以用i <= √n 来替换,因为到了后面其实是进行了重复的判断。
那么就可以优化为:
bool isPrime(int n){
if(n<2){
return false;
}
for(int i = 2; i <= sqrt(n); i++){
if(n % i == 0){
return false;
}
}
return true;
}
时间复杂度:O(n * √n )。
过了吗,当然没过,这次卡在了第21个测试点,起码说明我们的优化是有效果的,那就继续进行。
首先在自然数中,2和3是很常见的约数,我们是不是可以将其去处呢,当然可以。对于一个数对6取余,如果其模1或5,那么自然就不是2和3的公倍数。
int isPrime(int n){
if(n==2||n==3|n==5)return 1;
if(n==1||n==4||n==0)return 0;
if(n%6==1||n%6==5){
for(int i = 2 ; i <= sqrt(n) ; i++){
if(n%i==0)return 0;
}return 1;
}
return 0;
}
当然,还是过不了。emm,说明在数据量很大的情况下,只是单纯的优化判断每个数是不是质数是不行的,我们要减少对数的判断。比如一个很大的质数它的倍数,用上面的算法可能就得判断很多次。我们是不是可以避免这样的判断呢。接下来,请看质数筛。
埃氏筛:
int isPrime[5000000];//定义在全局变量的数组初始值为0,不用遍历赋值0。
int countPrimes(int n) {
if (n < 2) {
return 0;
}
int sum = 0;
for (int i = 2; i < n; ++i) {
if (isPrime[i] == 0) {
sum += 1;
if (i <= sqrt(n)) {
for (int j = i * i; j < n; j += i) {
isPrime[j] = 1;
}
}
}
}
return sum;
}
时间复杂度:O(nloglogn),证明比较麻烦,作者也就大概看懂,这里就不说出来了,(避免丢人)咳咳。
空间复杂度:O(n)。
最后,其实还有个更nb的线性筛时间复杂度和空间复杂度都为O(n)。这个我还得捋一捋,本质上还是在埃氏筛上进行了优化。等我学会了再进行补充。