前言:本人写这篇博客的主要目的是复习,所以这些筛法直接跳到 O ( n ) O(n) O(n)的了。
质数的线性筛
对于质数的筛法,传统的是暴力枚举(废话 ),现在我们讲一讲稍微精明一点的方法。我们可以考虑用质数的乘积进行筛除,由唯一分解定理,每个合数必定是有不止一个质数因子构成的。利用已找到的质数进行筛除,找出并标记合数。当搜到的当前质数没有被标记时,证明它没有更小的素因子,将这个数加入质数中。没错,这就是埃氏筛法。
int pcnt = 0;
memset( vis , 0 , sizeof(vis) );
memset( prime , 0 , sizeof(prime) );
for ( int i = 2 ; i <= n ; ++i ) {
if ( !vis[i] ) {
prime[++pcnt] = i;
}
for ( int j = 1 ; j <= pcnt ; ++j ) {
if ( i * prime[j] > n ) {
break;
}
vis[i * prime[j]] = 1;
}
}
不难证明这个筛法的正确性,对于任意一个合数
x
x
x,提出它的一个质因子为
p
p
p,则
x
x
x可表示成
p
∗
t
p * t
p∗t,
t
<
x
t < x
t<x,在循环的过程中一定有一种情况为
t
>
p
t > p
t>p,在满足上一个条件时一定在循环到
t
t
t的时候通过质数
p
p
p筛出
x
x
x。正确性得证。
不过,稍加思考就能发现,这种筛法会出现一个数被筛除多次的情况,那么,我们怎么避免这种情况呢?最好的办法就是让每个合数只会被自己最小的质因子筛除。具体实现也不算难,在自己代码一个合适的地方加上这一句即可:
if ( i % prime[j] == 0 ) {
break;
}
具体原理是这样的,根据唯一分解定理,对于一个合数
n
n
n,可表示成
n
=
p
1
k
1
∗
p
1
k
1
∗
⋅
⋅
⋅
∗
p
m
k
m
n = p_1^{k_1} * p_1^{k_1} * \cdot \cdot \cdot * p_m^{k_m}
n=p1k1∗p1k1∗⋅⋅⋅∗pmkm其中
p
m
>
⋅
⋅
⋅
>
p
2
>
p
1
p_m > \cdot \cdot \cdot > p_2 > p_1
pm>⋅⋅⋅>p2>p1,当我们用
p
1
p_1
p1筛出这个数时,要防止
p
2
p_2
p2也会筛出这个数,就要想办法让
n
p
2
{n \over p_2}
p2n未到数
p
2
p_2
p2时就停止筛除,也即让这个数最多筛到
p
1
p_1
p1,最简单的检测方法就是查看某个数是否有质因子
p
1
p_1
p1,当发现它为质因子
p
1
p_1
p1的倍数时,停止筛除。
归纳上面的思路,就是每次循环得到一个数,将这个数与已筛到的质数用乘积的方法筛去合数,当当前数的因子含有循环到的质数时,做完最后一次筛除就退出质数的循环,处理下一个数。
int pcnt = 0;
memset( vis , 0 , sizeof(vis) );
memset( prime , 0 , sizeof(prime) );
for ( int i = 2 ; i <= n ; ++i ) {
if ( !vis[i] ) {
prime[++pcnt] = i;
}
for ( int j = 1 ; j <= pcnt ; ++j ) {
if ( i * prime[j] > n ) {
break;
}
vis[i * prime[j]] = 1;
if ( i % prime[j] == 0 ) {//不要放错位置了,必须在它上一行代码后面
break;
}
}
}
欧拉函数及其线性筛
欧拉函数:
表示为
ϕ
(
p
)
\phi(p)
ϕ(p),意义是1到
p
p
p与
p
p
p互质(最大公因数等于1)的数的个数(
ϕ
(
1
)
=
1
\phi(1) = 1
ϕ(1)=1)。
性质1:当
p
p
p为质数时,
ϕ
(
p
)
=
p
−
1
\phi(p)=p-1
ϕ(p)=p−1。
性质2:当
p
p
p为质数时,且
i
%
p
=
0
i \% p = 0
i%p=0,那么
ϕ
(
i
∗
p
)
=
p
∗
ϕ
(
i
)
\phi(i * p) = p * \phi(i)
ϕ(i∗p)=p∗ϕ(i)。
证明:首先,考虑两个数
n
n
n和
i
i
i,如果两个数不互质,设
G
C
D
(
n
,
i
)
=
d
GCD(n,i) = d
GCD(n,i)=d,则
n
=
d
∗
x
n=d*x
n=d∗x,
i
=
d
∗
y
i=d*y
i=d∗y,其中
x
x
x,
y
y
y互质,所以
n
+
i
=
d
∗
(
x
+
y
)
n + i = d*(x+y)
n+i=d∗(x+y),即
G
C
D
(
n
+
i
,
i
)
=
G
C
D
(
n
,
i
)
GCD(n+i,i) = GCD(n,i)
GCD(n+i,i)=GCD(n,i);同时也不难看出,若
i
i
i与
n
n
n互质,则
i
+
n
i+n
i+n与
i
i
i也互质。
所以,区间
[
1
,
i
]
[1,i]
[1,i]与
i
i
i不互质的数有
i
−
ϕ
(
i
)
i-\phi(i)
i−ϕ(i)个,区间
(
i
+
1
,
i
+
i
]
(i+1,i+i]
(i+1,i+i]与
i
i
i不互质的数也有
i
−
ϕ
(
i
)
i-\phi(i)
i−ϕ(i)个。以此类推,区间
[
1
,
i
∗
p
]
[1,i * p]
[1,i∗p]与
i
i
i不互质的数有
p
∗
(
i
−
ϕ
(
i
)
)
p * (i-\phi(i))
p∗(i−ϕ(i))个。又因为
i
%
p
=
0
i \% p = 0
i%p=0,
i
∗
p
i * p
i∗p相对
i
i
i不会有新的因数,所以,区间
[
1
,
i
∗
p
]
[1,i * p]
[1,i∗p]与
i
∗
p
i * p
i∗p不互质的数有
p
∗
(
i
−
ϕ
(
i
)
)
p * (i-\phi(i))
p∗(i−ϕ(i))个,即
ϕ
(
i
∗
p
)
=
p
∗
ϕ
(
i
)
\phi(i * p) = p * \phi(i)
ϕ(i∗p)=p∗ϕ(i)。
性质3:当
p
p
p为质数时,且
i
%
p
i \% p
i%p不等于0,那么
ϕ
(
i
∗
p
)
=
(
p
−
1
)
∗
ϕ
(
i
)
\phi(i * p) = (p - 1)* \phi(i)
ϕ(i∗p)=(p−1)∗ϕ(i)。
证明:因为根据欧拉函数的积性性质,当
g
c
d
(
a
,
b
)
=
1
gcd(a,b) = 1
gcd(a,b)=1时,
ϕ
(
a
∗
b
)
=
ϕ
(
a
)
∗
ϕ
(
b
)
\phi(a*b)=\phi(a) * \phi(b)
ϕ(a∗b)=ϕ(a)∗ϕ(b),对于质数
p
p
p,
ϕ
(
p
)
=
p
−
1
\phi(p) = p - 1
ϕ(p)=p−1,所以性质3等价于欧拉函数的积性性质。
接下来,我们证明欧拉函数的积性性质,有兴趣的继续往下看:
在证明欧拉函数的积性性质前,我先给大家安利一个概念:完全剩余系。
如果
a
a
a,
b
b
b关于模
m
m
m同余,那么
a
a
a与
b
b
b同属一类,否则不同属于一类,这样可得
m
m
m个类,即
M
i
=
{
i
+
k
∗
m
∣
k
∈
Z
}
,
i
=
0
,
1
,
2
,
⋯
 
,
m
−
1
M_i=\{i + k*m | k \in Z \} , i = 0,1,2,\cdots ,m-1
Mi={i+k∗m∣k∈Z},i=0,1,2,⋯,m−1
它们称为模
m
m
m的剩余类。
从每个剩余类中各取一个数作为代表,这样得到的
m
m
m个数称为模
m
m
m的一个完全剩余系,简称完系。
完全剩余系还有一个性质,就是如果
G
C
D
(
n
,
m
)
=
1
GCD(n,m)=1
GCD(n,m)=1时,那么在
a
1
,
a
2
,
⋯
 
,
a
m
a_1,a_2,\cdots,a_m
a1,a2,⋯,am是模
m
m
m的一个完全剩余系时,那么
n
∗
a
1
+
k
,
n
∗
a
2
+
k
,
⋯
 
,
n
∗
a
m
+
k
n * a_1 + k,n * a_2+k,\cdots ,n * a_m + k
n∗a1+k,n∗a2+k,⋯,n∗am+k,模
m
m
m互不同余,也是一个完全剩余系。
证明:反证法,若
n
∗
a
i
+
k
≡
n
∗
a
j
+
k
(
m
o
d
m
)
n * a_i + k \equiv n * a_j + k \pmod{m}
n∗ai+k≡n∗aj+k(modm),又因为
G
C
D
(
n
,
m
)
=
1
GCD(n,m)=1
GCD(n,m)=1,不难得出
a
i
≡
a
j
(
m
o
d
m
)
a_i \equiv a_j \pmod{m}
ai≡aj(modm),与定义不成立,矛盾,故这个性质成立。
下面,回到对欧拉函数积性的证明:
设
G
C
D
(
a
,
b
)
=
1
GCD(a,b) = 1
GCD(a,b)=1且
a
,
b
∈
Z
a,b \in Z
a,b∈Z
ϕ
(
a
∗
b
)
\phi(a*b)
ϕ(a∗b)就是下面的
a
a
a行
b
b
b列的数表中与
a
∗
b
a*b
a∗b互质的数的个数:
1
2
⋯
i
⋯
b
,
b
+
1
b
+
2
⋯
b
+
i
⋯
2
∗
b
,
2
∗
b
+
1
2
∗
b
+
2
⋯
2
∗
b
+
i
⋯
3
∗
b
,
⋮
⋮
⋱
⋮
⋱
⋮
(
a
−
1
)
∗
b
+
1
(
a
−
1
)
∗
b
+
2
⋯
(
a
−
1
)
∗
b
+
i
⋯
a
∗
b
\begin{matrix} 1 & 2& \cdots & i& \cdots & b,\\ b + 1& b + 2& \cdots & b + i& \cdots & 2 * b,\\ 2 * b + 1& 2 * b + 2& \cdots & 2 *b + i& \cdots & 3 * b,\\ \vdots & \vdots & \ddots & \vdots& \ddots& \vdots\\ (a-1)*b+1& (a-1)*b+2 & \cdots & (a-1)*b+i & \cdots & a * b \end{matrix}
1b+12∗b+1⋮(a−1)∗b+12b+22∗b+2⋮(a−1)∗b+2⋯⋯⋯⋱⋯ib+i2∗b+i⋮(a−1)∗b+i⋯⋯⋯⋱⋯b,2∗b,3∗b,⋮a∗b
在第一行中有
ϕ
(
b
)
\phi(b)
ϕ(b)个数与
b
b
b互质,其余的数与
b
b
b不互质,根据上面证明性质2的思路不难得出第一行不与
b
b
b互质的数所在的列中的每个数都不与
b
b
b互质,肯定也不与
a
∗
b
a*b
a∗b互质,去掉这些列,不难得出剩下的
ϕ
(
b
)
\phi(b)
ϕ(b)列,同样根据上面证明性质2的思路,剩下的数都与
b
b
b互质。
0 , 1 , ⋯   , a − 1 0,1,\cdots,a-1 0,1,⋯,a−1构成模 a a a的一个完全剩余系,由于 a a a与 b b b互质,根据上面已证的完系的性质,得出 0 ∗ b + i , 1 ∗ b + i , ⋯   , ( a − 1 ) ∗ b + i 0*b+i,1*b+i,\cdots,(a-1)*b+i 0∗b+i,1∗b+i,⋯,(a−1)∗b+i也是模 a a a的一个完系。
与 b b b一样,其中有 ϕ ( a ) \phi(a) ϕ(a)个数与 a a a互质,按照 b b b的思路,不难发现每列都只有 ϕ ( a ) \phi(a) ϕ(a)个数与 a a a互质,而一共有 ϕ ( b ) \phi(b) ϕ(b)个列,所以总共有 ϕ ( a ) ∗ ϕ ( b ) \phi(a) * \phi(b) ϕ(a)∗ϕ(b)个数既与 a a a又与 b b b互质,即与 a ∗ b a*b a∗b互质,所以欧拉函数的积性得证。
再插一句:结合性质1与性质2,对于一个质数
p
p
p,有
ϕ
(
p
a
)
=
(
p
−
1
)
∗
p
a
−
1
\phi(p^{a})=(p-1)*p^{a-1}
ϕ(pa)=(p−1)∗pa−1
再结合唯一分解定理以及欧拉函数的积性,不难得出对于任意一个合数若按照下面分解成质数幂乘积表达式
n
=
p
1
a
1
∗
p
2
a
2
∗
⋯
∗
p
m
a
m
n=p_1^{a_1}*p_2^{a_2}* \cdots * p_m^{a_m}
n=p1a1∗p2a2∗⋯∗pmam,则可得出
ϕ
(
n
)
=
n
∗
(
1
−
1
p
1
)
∗
(
1
−
1
p
2
)
∗
⋯
∗
(
1
−
1
p
m
)
\phi(n) = n * (1-{ 1 \over p_1 }) * (1-{ 1 \over p_2 }) * \cdots * (1-{ 1 \over p_m })
ϕ(n)=n∗(1−p11)∗(1−p21)∗⋯∗(1−pm1)
这可用于快速求出一个数的欧拉函数值。
鉴于性质1,2,3均与质数有关,不难发现这可以套在质数线性筛里面,保证比1大的数都能被筛到,达到线性筛欧拉函数的效果。
int pcnt = 0;
memset( vis , 0 , sizeof(vis) );
memset( prime , 0 , sizeof(prime) );
phi[1] = 0;
for ( int i = 2 ; i <= n ; ++i ) {
if ( !vis[i] ) {
prime[++pcnt] = i;
phi[i] = i - 1;//性质1
}
for ( int j = 1 ; j <= pcnt ; ++j ) {
if ( i * prime[j] > n ) {
break;
}
vis[i * prime[j]] = 1;
if ( i % prime[j] == 0 ) {
phi[i * prime[j]] = phi[i] * prime[j];//性质2
break;
} else {
phi[i * prime[j]] = phi[i] * ( prime[j] - 1 );//性质3
}
}
}
本文参考资料:《初等数论的知识与问题》(单壿 著)
《信息学竞赛之数学一本通》