【瞎口胡】基础数学 3 筛法

写在前面

还记得第一篇的内容吗?不记得了可以去复习一下。

这一篇将会用到它们。

Eratosthenes 筛法

译名埃拉托色尼筛法。用于筛选 ([1,n]) 范围内的素数。

其算法思想是,一个质数的所有倍数都不是质数。每当我们筛选到一个质数 (k) 时,我们就把 (xk (x>1 and xk leq n)) 标记为合数。

bool vis[500010];
int n;
...
scanf("%d",&n);
for(int i=2;i<=n;++i){
    if(!vis[i]){
        for(int j=1;i*j<=n;++j){
        	vis[i*j]=true;
		}
	}
}
// 注意 1 不是质数,因此不能简单地用 !vis[x] 来判断 x 是否为质数,应为 !vis[x]&&x!=1

其时间复杂度为 (O(n log log n)),近似于线性。证明较为复杂,感兴趣的读者可以自行查阅。

但我们仍然有严格 (O(n)) 的算法。

线性筛法 / 欧拉筛法

Eratosthenes 不够优秀的原因之一是一个合数可能被它的多个质因子筛掉。为此,我们引入线性筛法,对于每个合数,它只会被自己的最小质因子筛掉,因此是 (O(n))

假设我们已经知道了 (leq i) 的正整数中,每个数是质数与否。令 (vis_i) 表示某个数是否被标记为质数((vis_i=1) 即为合数,反之则为质数),(operatorname{prime}_{1},operatorname{prime}_2,...,operatorname{prime}_s) 表示 (leq i) 的质数序列。不妨假设 (operatorname{prime}) 序列单调递增。

对于这样的 (i),我们依次将 (vis_{i imes operatorname{prime}_j}(i imes operatorname{prime}_j leq n and 1 leq j leq s)) 标记为 (1)。如果在这个过程中发现存在某个 (j) 使得 (operatorname{prime}_j mid i),则不再进行后续的标记。

这样做的正确性是有保证的。假设出现 (operatorname{prime}_j mid i) 时有 (j=v),那么有:

  1. 所有 (j leq v)(注意不是 (<))都满足 (operatorname{prime}_j)(i imes operatorname{prime}_j) 的最小质因子。
原文地址:https://www.cnblogs.com/liuzongxin/p/14864268.html