素数筛与欧拉函数入门

前言

我们在做题时会碰到素数,对于单个数据或者小范围数据,直接对每一个判断是不是素数,但如果碰到大范围数据或者重复使用数据,这样做往往会超时,就需要快速挑选出素数并保存,这就是素数筛,利用它们可以求欧拉函数

素数筛

暴力筛,时间复杂度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的倍数剔除掉;不断重复下去…。
素数筛与欧拉函数入门_第1张图片

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)

在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的
素数筛与欧拉函数入门_第2张图片

//筛选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互质。

素数筛与欧拉函数入门_第3张图片

举例

φ ( 12 ) = 12 × ( 1 − 1 / 2 ) × ( 1 − 1 / 3 ) = 4 φ(12)=12×(1-1/2)×(1-1/3)=4 φ(12)=12×(11/2)×(11/3)=4
φ ( 10 ) = 10 × ( 1 − 1 / 2 ) × ( 1 − 1 / 5 ) = 4 φ(10)=10×(1-1/2)×(1-1/5)=4 φ(10)=10×(11/2)×(11/5)=4

性质

(1)若n为质数, φ ( n ) = n − 1 φ(n)=n-1 φ(n)=n1

(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=223那么 φ ( 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=φ43=φ2231=22213130=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=φ103=42=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;}


你可能感兴趣的:(c++,算法,c++)