Loading

筛法复习

首先考虑筛素数, 朴素的判断素数复杂度 \(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)\)​ , 是一种很优秀的筛法, 并且是一种筛积性函数的通用筛法

posted @ 2021-09-14 09:17  —O0oO-  阅读(41)  评论(0编辑  收藏  举报