index > Algebra > prime sieve
主要内容
本篇只收录较快的线性筛法,作用就是求2到n的所有素数,顺便得到判断数组。
单纯求素数,标记所有合数。(常用)
const int MAXN = -1;//10000005
int prime[MAXN],pnum;
bool is_composite[MAXN];
void sieve(const int &n) {
// 1 is exception
//clock_t begin = clock();
for (int i = 2; i < n; ++i) {
if (!is_composite[i]) prime[++pnum]=i;
for (int j = 1; j <=pnum && i * prime[j] < n; ++j) {
is_composite[i * prime[j]] = true;
if (i % prime[j] == 0) break;
}
}
//cout << (double) (clock() - begin) / CLOCKS_PER_SEC << endl;
}
求素数的同时,求所有合数最小的质因子。
const int MAXN = -1;//10000005
int prime[MAXN], pnum;
int min_composite[MAXN];
void sieve(const int &n) {
// 1 is exception
//clock_t begin = clock();
for (int i = 2; i < n; ++i) {
if (!min_composite[i]) {
prime[++pnum] = i;
min_composite[i] = i;
}
for (int j = 1; j <= pnum && prime[j] <= min_composite[i] && i * prime[j] < n; ++j) {
min_composite[i * prime[j]] = prime[j];
// if (i % prime[j] == 0) break;
}
}
//cout << (double) (clock() - begin) / CLOCKS_PER_SEC << endl;
}
&& prime[j] <= min_composite[i]
和if (i % prime[j] == 0) break;
相差无几,任选其一即可。
理解
Sieve of Eratosthenes 的思路很好理解,每次装入一个素数就把所有这个数的倍数都划掉。但是容易发现,很多数会被多次划掉。时间复杂度是 O ( n l o g l o g n ) O(n log log n ) O(nloglogn)的,不太理想。
Euler’s sieve 的角度其实不太一样(尽管两者代码很相似)。为了理解,最好优先看第二份代码。下面是我的个人理解。
既然不希望合数被重复删去,那么应该尽量让所有合数是被最小素因子去掉的。这就构成了一种转移。假设 p i pi pi是 i i i的最小素因子,那么对于所有小于等于 p i pi pi的素数 p j pj pj有, p j ∗ i pj*i pj∗i是一个合数,并且以 p j pj pj为最小素因子。
当枚举到 i i i的时候,已经有了不超过 i i i的所有最小素数,那就可以通过遍历 i ∗ p r i m e [ j ] i * prime_{[j]} i∗prime[j] 依此把后面的数给删去。
被删去的状态,是从最小素数集合 p r i m e [ j ] prime_{[j]} prime[j]中转移过来的,所以是可以理解新的合数是被最小的素因子删去了。
但不能一直删,当$ i % prime[j] == 0$ 成立,说明此时的 p r i m e [ j ] prime[j] prime[j]恰好是$ i 的 最 小 素 数 , 的最小素数, 的最小素数,i * prime_{[j+1]} 之 后 的 倍 数 , 应 该 由 之后的倍数,应该由 之后的倍数,应该由i$作为最小素因子时删去。
$ prime[j] <= min_composite[i]$也是类似的判断。
虽然不知道如何验证,但是实事便是,所有的数都可以被常数次遍历。一般情况我们不需要最小素因子,所以可以优化成布尔型经行遍历。
花边
这个算法最早的渊源是和欧拉有关的,但好像不是欧拉自己提出的。
Euler’s proof of the zeta product formula contains a version of the sieve of Eratosthenes in which each composite number is eliminated exactly once.[8] The same sieve was rediscovered and observed to take linear time by Gries & Misra (1978).[19] It, too, starts with a list of numbers from 2 to n in order.
后人观测到了这个算法就发了一篇CACM期刊论文,而巨人欧拉只是以这个方法用来辅助一下更重要的证明(黎曼),没当回事儿···ORZ
但线性筛的概念,可以拓展到类积性函数的求法。去理解这个算法有更长远的意义。
参考
Math note — linear sieve By Nisiyama_Suzune 这篇博客有讲到线性筛