素数筛法
一、定义
素数指质数,指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数;
二、唯一分解定理
唯一分解定理又称为算术基本定理,可描述为,
任何一个大于 1 的自然数 N N N ,如果 N N N 不为质数,那么 N N N 可以唯一分解成有限个质数的乘积,
N = p 1 a 1 ∗ p 2 a 2 ∗ p 3 a 3 . . . . . . p n a n N = p_1^{a_1} * p_2^{a_2} * p_3^{a_3} ...... p_n^{a_n} N=p1a1∗p2a2∗p3a3......pnan
这里 p 1 < p 2 < p 3 < . . . < p n p_1 < p_2 < p_3 < ... < p_n p1<p2<p3<...<pn 均为质数,其中指数 a i a_i ai 为正整数,这样的分解称为 N N N 的标准分解式;
三、埃氏筛法
1. 说明
素数筛法,是一种快速筛出 2 ∼ n 2 \sim n 2∼n 中之间所有素数的方法。朴素的筛法叫做埃氏筛 (The Sieve of Eratosthenes,埃拉托色尼筛) ;
其核心思想为,遍历到质数时,将其的倍数筛掉;
2. 过程
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
从前向后看,找到第一个未被划掉的数为 2,为质数,把 2 的倍数 (不包括 2 ) 删除,即删除 4, 6, 8, 10, 12, 14, 16;
2 | 3 | 5 | 7 | 9 | 11 | 13 | 15 |
---|
下一个未被划掉的数是 3,为质数,把 3 的倍数相除,即删除 6, 9, 12, 15 (注意 6 和 12 虽然为 3 的倍数,但是它是被 2 筛除的) ;
2 | 3 | 5 | 7 | 11 | 13 |
---|
下一个未被划掉的数是 5,但是 5 已经超过 16 \sqrt{16} 16 了,所以遍历结束,剩下未被划掉的数都是素数:
2 | 3 | 5 | 7 | 11 | 13 |
---|
3. 关键代码
bool f[MAXN];//f用来标记i是否为素数,0为素数,1为合数;
void prime(int n) {
for (int i = 2; i * i <= n; i++) {
if (!f[i]) {
for (int j = i * i; j <= n; j += i) {
f[j] = 1;
}
}
}
}
4. 问题
埃氏筛法在筛数时会筛到同一个数;
四、欧拉筛法
1. 说明
欧拉筛,也叫线性筛,可以在 O ( n ) O(n) O(n) 时间内完成对 2 ∼ n 2 \sim n 2∼n 的筛选;
其核心思想为,让每一个合数被其最小质因数筛到;
2. 过程
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
质数表如下,
从头到尾遍历,第一个数是 2 ,未被划掉,把它放进质数表;
然后用 2 去乘质数表里的每一个数并删除,这里只删除 4 ;
2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
质数表如下,
2 |
---|
下一个是 3,加入质数表划掉 6,9 ;
2 | 3 | 5 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
质数表如下,
2 | 3 |
---|
下一个数是 4 (删除的也要遍历,只是不加入质数表),先删除 8,但不删除 12,因为 12 = 2 ∗ 6 12 = 2 * 6 12=2∗6 应该由它的最小质因数 2 筛掉,而不是 3 ;
2 | 3 | 5 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|
质数表如下,
2 | 3 |
---|
实际上,对于 x x x ,遍历到质数表中的 P P P ,且发现 p ∣ x p|x p∣x 时,就应当停止遍历质数表;
证明如下,
设 x = p ∗ r ( r ≥ p ) x = p * r (r \geq p) x=p∗r(r≥p) , p p p 本身是 x x x 的最小质因数,则对于 ∀ p ′ > p \forall p' > p ∀p′>p ,有 p ′ x = p p ′ r = ( p p ′ ) r p'x = pp'r = (pp')r p′x=pp′r=(pp′)r ,则说明 p ′ x p'x p′x 的最小质因数不是 p ′ p' p′ ,而是 p p p 所以不应该在此删除它;
下一个是 5,加入质数表,删除 10,15 ;
2 | 3 | 5 | 7 | 11 | 13 | 14 | 16 |
---|
质数表如下,
2 | 3 | 5 |
---|
3. 关键代码
int prime[MAXN];//用来存放质数表
bool f[MAXN];//f用来标记i是否为素数,0为素数,1为合数;
void Prime(int n) {
for (int i = 2; i <= n; i++) {
if (!f[i]) {
prime[++cnt] = i;
}
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
f[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}