两种筛法
埃氏筛法
筛掉所有质数的倍数。每找到一个质数就把它所有的倍数筛掉。
class Solution {
public int countPrimes(int n) {
int[] primes = new int[n + 1];
int cnt = 0;
boolean[] st = new boolean[n + 1];
for(int i = 2; i < n; i++){
if(!st[i]){
primes[cnt ++] = i;
for(int j = i + i; j < n; j += i) st[j] = true;
}
}
return cnt;
}
}
时间复杂度
O ( n / 2 + n / 3 + n / 5 + . . . ) = O ( n l o g l o g n ) O(n / 2 + n / 3 + n / 5 + ...) = O(nloglogn) O(n/2+n/3+n/5+...)=O(nloglogn)
线性筛
每次只拿最小质因子筛掉合数,换言之,如果一个数被筛掉了,那么一定是被它的最小质因子筛掉的。
- 当 i i%primes[j]!=0 i时,说明此时遍历到的 p r i m e s [ j ] primes[j] primes[j]不是i的质因子,那么只可能是此时的 p r i m e s [ j ] < i primes[j]<i primes[j]<i的最小质因子,所以 p r i m e s [ j ] ∗ i primes[j]*i primes[j]∗i的最小质因子就是 p r i m e s [ j ] primes[j] primes[j];
- 当有 i i%primes[j]==0 i时,说明 i i i的最小质因子是 p r i m e s [ j ] primes[j] primes[j],因此KaTeX parse error: Undefined control sequence: \* at position 11: primes[j] \̲*̲ i的最小质因子也就应该是 p r i m e [ j ] prime[j] prime[j],之后接着用 s t [ p r i m e s [ j + 1 ] ∗ i ] = t r u e st[primes[j+1]*i]=true st[primes[j+1]∗i]=true去筛合数时,就不是用最小质因子去更新了,因为 i i i有最小质因子 p r i m e s [ j ] < p r i m e s [ j + 1 ] primes[j]<primes[j+1] primes[j]<primes[j+1],此时的 p r i m e s [ j + 1 ] primes[j+1] primes[j+1]不是 p r i m e s [ j + 1 ] ∗ i primes[j+1]*i primes[j+1]∗i的最小质因子,此时就应该退出循环,避免之后重复进行筛选。
class Solution {
public int countPrimes(int n) {
int[] primes = new int[n + 1];
int cnt = 0;
boolean[] st = new boolean[n + 1];
for(int i = 2; i < n; i++){
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; primes[j] <= n / i; j ++) {
st[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
return cnt;
}
}
时间复杂度
每个数只会被自己的最小质因子筛掉一次,因此是线性时间复杂度 O ( n ) O(n) O(n)。