前言
我们在做题时会碰到素数,对于单个数据或者小范围数据,直接对每一个判断是不是素数,但如果碰到大范围数据或者重复使用数据,这样做往往会超时,就需要快速挑选出素数并保存,这就是素数筛,利用它们可以求欧拉函数
素数筛
暴力筛,时间复杂度O( n n n\sqrt{n} nn)
遍历判断保存完事
bool isprime(int n) //暴力解法,时间复杂度为O(n*n)
{
for(int i=2; i<=sqrt(n); i++)
if(n%i==0) return false;
return true;
}
int Make_prime(int n)
{
int cnt=0;
for(int i=2; i<=n; i++)
if(isprime(i))
prime[cnt++]=i;
return cnt;
}
重复用可能可以,但是大规模数据也是超时
埃拉托斯特尼筛法,O( n l o g l o g n nlog log n nloglogn)
要得到自然数n以内的全部素数,必须把不大于 n \sqrt{n} n的所有素数的倍数剔除,剩下的就是素数。
给出要筛数值的范围n,找出以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去…。
ll Make_prime(ll n)
{
ll cnt=0;
memset(vis,0,sizeof(vis));
for(ll i=2; i<=n; i++)
{
if(!vis[i])
{
prime[cnt++]=i;
for(ll j=2*i; j<=n; j+=i)
vis[j]=1;
}
}
return cnt;
}
线性筛素数(欧拉筛)O(n)
在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的
//筛选1~n中所有的素数,保存到prime数组中,cnt为素数的个数
//vis数组标记是否为素数,0表示是素数,1表示不是素数
int Make_Prime(int n)
{
int cnt = 0;
memset(vis, 0, sizeof(vis));
for (int i = 2; i <= n; i ++)
{
if (!vis[i])
prime[cnt++] = i;
for (int j = 0; j < cnt && i * prime[j] <=n; j ++)
{
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) //关键
break;
}
}
return cnt;
}
欧拉函数
在数论,对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目(因此φ(1)=1)。此函数以其首名研究者欧拉命名(Euler’s totient function),它又称为Euler’s totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。
举例
φ ( 12 ) = 12 × ( 1 − 1 / 2 ) × ( 1 − 1 / 3 ) = 4 φ(12)=12×(1-1/2)×(1-1/3)=4 φ(12)=12×(1−1/2)×(1−1/3)=4
φ ( 10 ) = 10 × ( 1 − 1 / 2 ) × ( 1 − 1 / 5 ) = 4 φ(10)=10×(1-1/2)×(1-1/5)=4 φ(10)=10×(1−1/2)×(1−1/5)=4
性质
(1)若n为质数, φ ( n ) = n − 1 φ(n)=n-1 φ(n)=n−1
(2)若n是质数p的k次幂(即n=pk,φ(n)=pk-p(k-1)=(p-1)p(k-1) ,因为除了p的倍数外,其他数都跟n互质
比如 12 = 2 ∗ 2 ∗ 3 12=2*2*3 12=2∗2∗3那么 φ ( 12 ) = φ ( 4 ∗ 3 ) = φ ( 2 2 ∗ 3 1 ) = ( 2 2 − 2 1 ) ∗ ( 3 1 − 3 0 ) = 4 φ(12)=φ(4*3)=φ(2^2*3^1)=(2^2-2^1)*(3^1-3^0)=4 φ(12)=φ(4∗3)=φ(22∗31)=(22−21)∗(31−30)=4
(3)欧拉函数是积性函数——若m,n互质, φ ( m n ) = φ ( m ) φ ( n ) φ(mn)=φ(m)φ(n) φ(mn)=φ(m)φ(n)
φ ( 30 ) = φ ( 10 ∗ 3 ) = 4 ∗ 2 = 8 φ(30)=φ(10*3)=4*2=8 φ(30)=φ(10∗3)=4∗2=8

(4)若m%n==0 , φ ( m n ) = n ∗ φ ( m ) φ(mn)=n*φ(m) φ(mn)=n∗φ(m)
(5)特殊性质:当n为奇质数时, φ ( 2 n ) = φ ( n ) φ(2n)=φ(n) φ(2n)=φ(n)
(6)对任何两个互质的正整数a, m(m>=2)有aφ(m)≡1(mod m),即欧拉定理
当m是质数p时,此式则为:a(p-1)≡1(mod p),即费马小定理
求单个数欧拉函数
int eular(int n) //求单个数的欧拉函数
{
int ans = n;
for(int i = 2; i*i <= n; i++)
{
if(n % i == 0)
{
ans = ans/i*(i-1); //ans=ans*(1-1/i)=ans*(i-1)/i
// =ans/i*(i-1) //先除防止溢出
while(n % i == 0) //消除i因子
n /= i;
}
}
if(n > 1)ans = ans/n*(n-1); //最后可能还剩下一个质因数没有除
return ans;
}
埃拉托斯特尼筛求欧拉函数
void euler(int n)
{
for (int i=1;i<=n;i++) phi[i]=i;
for (int i=2;i<=n;i++)
{
if (phi[i]==i)//这代表i是质数
{
for (int j=i;j<=n;j+=i)
{
phi[j]=phi[j]/i*(i-1); //把i的倍数更新掉
}
}
}
}
欧拉筛求欧拉函数
//phi[]欧拉函数 prime[]质数 vis[]标记
int euler(int n)
{
int cnt=0;
phi[1]=1;//1要特判
for (int i=2; i<=n; i++)
{
if (vis[i]==0)//这代表i是质数
{
prime[cnt++]=i; phi[i]=i-1; //性质1
}
for (int j=0; j<cnt&&prime[j]*i<=n; j++)
{
vis[i*prime[j]]=1;
if (i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];//性质4
break;
}
else phi[i*prime[j]]=phi[i]*phi[prime[j]];//性质3
}
} return cnt;}