leetcode204 --- 埃氏筛和线性筛

埃氏筛

我们考虑这样一个事实:如果 x x x 是质数,那么大于 x x x x x x 的倍数 2 x 2x 2x, 3 x 3x 3x,…一定不是质数,因此我们可以从这里入手。

我们设 i s P r i m e [ i ] isPrime[i] isPrime[i] 表示数 i i i 是不是质数,如果是质数则为 1 1 1,否则为 0 0 0。从小到大遍历每个数,如果这个数为质数,则将其所有的倍数都标记为合数(除了该质数本身),即 0 0 0,这样在运行结束的时候我们即能知道质数的个数。

这里还可以继续优化,因为对于一个质数 x x x,如果按上文说的我们从 2 x 2x 2x开始标记其实是冗余的,应该直接从 x ⋅ x x\cdot x xx 开始标记,因为 2 x , 3 x , … 2x,3x,… 2x,3x,这些数一定在之前就被其他数的倍数标记过了,例如 2 2 2的所有倍数, 3 3 3的所有倍数等。

class Solution {
    
    
public:
    int countPrimes(int n) {
    
    
        vector<int> isPrime(n,1);
        int res = 0;
        for(long long i=2;i<n;i++) {
    
    
            if(isPrime[i]) {
    
    
                ++res;
                for(long long j=i*i; j<n; j+=i) {
    
    
                    isPrime[j] = 0;
                }
            }
        }
        return res;
    }
};

线性筛

埃氏筛会有重复标记的行为,比如45会被3和5重复标记,线性筛则可以避免这种情况,线性筛又称欧拉筛,欧拉筛需要维护一个质数表,对于 2 ≤ i < n 2 \le i < n 2i<n ,当遍历到整数 i i i 时,执行如下操作,

1、如果 i i i 是质数,即 i i i 没有被标记为合数,则将 i i i 加入质数表

2、从小到大遍历质数列表,对每个质数 p r i m e prime prime ,当 i ∗ p r i m e < n i * prime < n iprime<n 时执行如下操作:

​ a、将 i ∗ p r i m e i*prime iprime 标记为合数

​ b、如果 i i i 能被 p r i m e prime prime 整除,则结束遍历质数列表

这里的原理是,如果如果 i i i 能被 p r i m e prime prime 整除,则记 p r i m e ′ prime' prime 为大于 p r i m e prime prime 的质数, y = x ∗ p r i m e ′ y=x*prime' y=xprime ,则有 y = x p r i m e ∗ p r i m e ′ ∗ p r i m e y = \frac{x}{prime} * prime' *prime y=primexprimeprime ,当遍历到 $\frac{x}{prime} * prime’ $ 时, y y y 会被质数 p r i m e prime prime 标记,由于 p r i m e < p r i m e ’ prime < prime’ prime<prime ,所以当 x p r i m e \frac{x}{prime} primex 为整数,即 x   m o d   p r i m e = = 0 x \ mod \ prime == 0 x mod prime==0 时,这个整数就可以被 p r i m e ′ prime' prime 标记,因此 y y y 会被其最小的质因数标记。

由于欧拉筛可以确保每个合数被其最小的质因数标记,因此可以确保每个合数只被标记一次。

class Solution {
    
    
public:
    int countPrimes(int n) {
    
    
        vector<int> isPrime(n, 1);
        vector<int> Prime;
        for (int i = 2; i < n; ++i) {
    
    
            if (isPrime[i]){
    
    
                Prime.push_back(i);
            } 
                
            for(auto j:Prime) {
    
    
                if ((long long)i * j < n) {
    
    
                    isPrime[i*j] = 0;
                } else {
    
    
                    break;
                }
                if (i % j == 0) break;
            }

        }
        return Prime.size();
    }
};

但是,看似线性筛操作的更少,实际上运行时间会更长,因为进行了更多的运算操作

猜你喜欢

转载自blog.csdn.net/weixin_43903639/article/details/129295578