欧拉线性筛求质数
欧拉线性筛法。
应用:
1.筛质数
不会重复筛除,是线性O(n)的复杂度。
const int MAXN=3000001;
int prime[MAXN];//保存素数
bool mark[MAXN];//初始化
int Prime(int n)
{
int cnt=0;
memset(mark,0,sizeof(mark));
for(int i=2;i<n;i++)
{
if(!mark[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<n;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)//关键
break;
}
}
return cnt;//返回小于n的素数的个数
}
首先,先明确一个条件,任何合数都能表示成一系列素数的积。
然后利用了每个合数必有一个最小素因子,每个合数仅被它的最小素因子筛去正好一次。所以为线性时间。
代码中体现在:
if(i%prime[j]==0)break;
prime数组 中的素数是递增的,当 i 能整除 prime[j],那么 i*prime[j+1] 这个合数肯定被 prime[j] 乘以某个数筛掉,因为某个数只会被它的最小的质因数更新,而这里不满足。
因为i中含有prime[j], prime[j] 比 prime[j+1] 小。接下去的素数同理。所以不用筛下去了。
在满足i%prme[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。
例子:
比如当前i=15,那么其最小素因数是3,而且它可以表示成3*5;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
那么筛到这里也就可以停止了,因为i*prime[j+1],也就是5*15=75,一定会在当i=25时被其最小素因数3更新。
2.求欧拉函数
利用欧拉函数的性质( p为质数 ):
1. phi(p)=p-1
2. 如果i mod p = 0, 那么 phi(i * p)=p * phi(i)
3.若i mod p ≠0, 那么 phi( i * p )=phi(i) * ( p-1 )
以上两条均可以用欧拉函数的表达式推出E(n)=n*(1-1/p1)(1-1/p2)…*(1-1/pn) 其中px是n的质因数。)
void getPhi()
{
int i,j,tot=0;
for(i=2;i<=MaxN;i++)
{
if(Mark[i]==false){ Prime[++tot]=i;phi[i]=i-1; }
//当 i 是素数时 phi[i]=i-1
for(j=1; j<=tot&&i*Prime[j]<=MaxN; j++)
{
Mark[i*Prime[j]]=true;
if(i%Prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
//如果i mod p = 0, 那么 phi[i * p]=p * phi[i]
else phi[i*Prime[j]]=phi[i]*(Prime[j]-1);
}
//其实这里Prime[j]-1就是phi[Prime[j]],利用了欧拉函数的积性
}
}
3.求组合数取模(分解质因数法)Cnm mod p
以C64为例:
C64=(6!)/(4!×2!)=((2×3)×(5)×(2×2)×(3)×(2)) / ((2×2)×(3)×(2)×(2))
我们记录分子分母中对应质数的个数:分子:Pri[2]=4,Pri[3]=2,Pri[5]=1
分母:Pri’[2]=4,Pri’[3]=1
上下抵消,剩下Pri[3]=1,Pri[5]=1