质数1:质数的线性筛,欧拉函数及其线性筛

质数1:质数的线性筛,欧拉函数及其线性筛


前言:本人写这篇博客的主要目的是复习,所以这些筛法直接跳到 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 pt t &lt; x t &lt; x t<x,在循环的过程中一定有一种情况为 t &gt; p t &gt; 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=p1k1p1k1pmkm其中 p m &gt; ⋅ ⋅ ⋅ &gt; p 2 &gt; p 1 p_m &gt; \cdot \cdot \cdot &gt; p_2 &gt; 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)=p1
性质2:当 p p p为质数时,且 i % p = 0 i \% p = 0 i%p=0,那么 ϕ ( i ∗ p ) = p ∗ ϕ ( i ) \phi(i * p) = p * \phi(i) ϕ(ip)=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=dx i = d ∗ y i=d*y i=dy,其中 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,ip] 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 ip相对 i i i不会有新的因数,所以,区间 [ 1 , i ∗ p ] [1,i * p] [1,ip] i ∗ p i * p ip不互质的数有 p ∗ ( i − ϕ ( i ) ) p * (i-\phi(i)) p(iϕ(i))个,即 ϕ ( i ∗ p ) = p ∗ ϕ ( i ) \phi(i * p) = p * \phi(i) ϕ(ip)=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) ϕ(ip)=(p1)ϕ(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) ϕ(ab)=ϕ(a)ϕ(b),对于质数 p p p ϕ ( p ) = p − 1 \phi(p) = p - 1 ϕ(p)=p1,所以性质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 , ⋯ &ThinSpace; , m − 1 M_i=\{i + k*m | k \in Z \} , i = 0,1,2,\cdots ,m-1 Mi={i+kmkZ},i=0,1,2,,m1
它们称为模 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 , ⋯ &ThinSpace; , a m a_1,a_2,\cdots,a_m a1,a2,,am是模 m m m的一个完全剩余系时,那么 n ∗ a 1 + k , n ∗ a 2 + k , ⋯ &ThinSpace; , n ∗ a m + k n * a_1 + k,n * a_2+k,\cdots ,n * a_m + k na1+k,na2+k,,nam+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} nai+knaj+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} aiaj(modm),与定义不成立,矛盾,故这个性质成立。
下面,回到对欧拉函数积性的证明
G C D ( a , b ) = 1 GCD(a,b) = 1 GCD(a,b)=1 a , b ∈ Z a,b \in Z a,bZ
ϕ ( a ∗ b ) \phi(a*b) ϕ(ab)就是下面的 a a a b b b列的数表中与 a ∗ b a*b ab互质的数的个数:
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 &amp; 2&amp; \cdots &amp; i&amp; \cdots &amp; b,\\ b + 1&amp; b + 2&amp; \cdots &amp; b + i&amp; \cdots &amp; 2 * b,\\ 2 * b + 1&amp; 2 * b + 2&amp; \cdots &amp; 2 *b + i&amp; \cdots &amp; 3 * b,\\ \vdots &amp; \vdots &amp; \ddots &amp; \vdots&amp; \ddots&amp; \vdots\\ (a-1)*b+1&amp; (a-1)*b+2 &amp; \cdots &amp; (a-1)*b+i &amp; \cdots &amp; a * b \end{matrix} 1b+12b+1(a1)b+12b+22b+2(a1)b+2ib+i2b+i(a1)b+ib,2b,3b,ab
在第一行中有 ϕ ( b ) \phi(b) ϕ(b)个数与 b b b互质,其余的数与 b b b不互质,根据上面证明性质2的思路不难得出第一行不与 b b b互质的数所在的列中的每个数都不与 b b b互质,肯定也不与 a ∗ b a*b ab互质,去掉这些列,不难得出剩下的 ϕ ( b ) \phi(b) ϕ(b)列,同样根据上面证明性质2的思路,剩下的数都与 b b b互质。

0 , 1 , ⋯ &ThinSpace; , a − 1 0,1,\cdots,a-1 0,1,,a1构成模 a a a的一个完全剩余系,由于 a a a b b b互质,根据上面已证的完系的性质,得出 0 ∗ b + i , 1 ∗ b + i , ⋯ &ThinSpace; , ( a − 1 ) ∗ b + i 0*b+i,1*b+i,\cdots,(a-1)*b+i 0b+i,1b+i,,(a1)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 ab互质,所以欧拉函数的积性得证。

再插一句:结合性质1与性质2,对于一个质数 p p p,有 ϕ ( p a ) = ( p − 1 ) ∗ p a − 1 \phi(p^{a})=(p-1)*p^{a-1} ϕ(pa)=(p1)pa1
再结合唯一分解定理以及欧拉函数的积性,不难得出对于任意一个合数若按照下面分解成质数幂乘积表达式 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=p1a1p2a2pmam,则可得出 ϕ ( 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(1p11)(1p21)(1pm1)
这可用于快速求出一个数的欧拉函数值。

鉴于性质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 
		}
	}
}

本文参考资料:《初等数论的知识与问题》(单壿 著)
《信息学竞赛之数学一本通》

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值