筛法复习

首先考虑筛素数, 朴素的判断素数复杂度 (O(sqrt n))

埃氏筛

前几天做 mit6.s081 lab 的时候看到的, 其实就是埃氏筛法

p = get a number from left neighbor
print p
loop:
    n = get a number from left neighbor
    if (p does not divide n)
        send n to right neighbor

image-20210913154718594

正确性是比较显然的,每一次 print 出来的数, 都是经过了比其小的素数的筛选, 说明其没有别的质因数。

for(int i = 2;i < N;i++){
    if(isprime[i]){
      	for(int j = 2 * i;j < N;j += i) isprime[j] = false; // 可以从 i*i 开始枚举
    }
}

调和级数 log 复杂度, 复杂度是 (O(nlogn))

线性筛

void init(){
    for(int i = 2;i < N;i++){
        if(!vis[i]) prime[++cnt] = i;
        for(int j = 1;j <= cnt && prime[j] * i < N;j++){
            vis[i * prime[j]] = 1;
            if(i % prime[j]) break;
        }
    }
}

可以把理解的重点放到 if(i % prime[j]) break;

对于每一个数 i​ , 都会用小于等于其最小质因数的素数 prime[j]​ ,把 i * prime[j] 筛去

对于每一个合数, 都会被其最小质因数筛去, 例如考虑

(n = p_1^{k_1}p_2^{k_2}...p_m^{k_m}) , 它肯定会被 (p_1) 筛去,并且只会被 (p_1)​​ 筛去

考虑它被 (p_2) 筛去, 则 prime[j] = p2 , i = n / p2

在枚举 j 的时候, 枚举到 (p_1) 的时候就 break 出去了,所以不会被 (p_2) 筛去, 其余同理

因而其复杂度是 (O(n))​ , 是一种很优秀的筛法, 并且是一种筛积性函数的通用筛法

原文地址:https://www.cnblogs.com/sduwh/p/15266057.html