線性篩——素數篩,莫比烏斯篩,歐拉函數篩

質數篩

暴力版數組

1 bool prime(int n) 2 { 3     for(int i=2;i<=sqrt(n);i++) 4         if(n%i==0) return false; 5     
6     return true; 7 }

若是給定區間1到n,判斷每個數是否是質數,每一個數都要從2開始暴搜,就某得靈魂。函數

普通篩:spa

 

bool isprime[maxn];//1表明是質數,0表明不是
void judge() { memset(isprime,true,sizeof(isprime))//默認全都是質數
    for(int i=2;i<=maxn;i++) { if(isprime(i)){//i是質數
            for(int j=2;j*i<=maxn;j++)//那麼i乘一個數就不是質數了
                isprime[j]=0; } } }

 

這樣就是一個線性的過程,從2開始到n篩,靈魂注入了一點,但弊端仍是有,不少數被重複判斷了。code

真•線性篩(歐拉篩):blog

 

bool isprime[n];//1表明是質數,0表明不是
int  prime[n];//素數數組,從小到大裝着1到n的素數
void judge() { memset(isprime,true,sizeof(isprime))//默認全都是質數
    int tot=0
    for(int i=2;i<=n;i++) { if(isprime(i))//是質數
            prime[tot++]=i;//存到素數數組裏

for(int j=0;j<tot&&prime[j]*i<n;j++) { isprime[ i * prime[j] ]=false; if(i%prime[j]==0)//保證每一個合數都被它的最小素數篩掉 break; } } }

 

這個的思想和普通篩同樣,但卻避免了一個合數被屢次判斷。get

        慢慢來,首先i不管是否是素數,i乘一個prime[j]確定就不是素數,如何避免重複篩呢,首先合數是必定有質因子的(合數能分解質因數),因此要找到j的界限,保證一個合數只被判斷一次,線性篩是從小到大的,天然是找每一個合數的最小質因子了。class

結論:若是 i 能整除 prime[j] ,此時j就不能++了, i *prime[j+1] 確定會被 prime[ j]乘某個數篩掉;im

 

證實:i 若能整除 prime[ j]d3

   則   i=k * prime[j]img

   則   i *prime[ j+1]=k*prime[ j ] *prime[j+1] 

     則   i * prime[ j + 1] = k' *prime[ j ]

 因此每一個合數都只會被它的最小質因數篩掉。

莫比烏斯篩

莫比烏斯函數:

如今若是要預處理處理1到n的莫比烏斯函數值,就能夠用到上面的素數篩了;

分析:

    對於mu(x),x是質數的話,mu(x)=-1;

    若是x%p==0(p是質數),就意味着x有p這個質因子,因此x*p這個數就有p2這個質因子了,因而mu(x*p)=0;

 

bool isprime[n]; int prime[n],mu[n]; void get() { memset(isprime,1,sizeof(isprime)); int tot=0; for(int i=2;i<n;i++) { if(isprime(i)) prime[tot++]=i,mu[i]=-1;//若是i是質數,初始化mu爲-1
        
        for(int j=1;j<=tot&&i*prime[j]<n;j++){ isprime[i*prime[j]]=0;//和素數篩同樣
            
            if(i%prime[j]==0){//素數篩的終止條件,
                mu[i*prime[j]]==0//對含有平方prime[j]因子的數mu值爲0
                break; } } } }

 歐拉函數篩

歐拉函數篩:

  歐拉函數:對整數n,歐拉函數是小於n的正整數中與n互質的數的數目。

  如今分析一下,若是p是質數,那麼phi(p)=p-1;

         若是n爲質數p的k次方,那麼phi(n)=pk-pk-1,由於一個數不包含質數p的時候纔會與n互質,那麼包換p的數有1*p,2*p·····,pk-1*p共pk-1個,因此phi(n)=n個減p的k-1次方個。(n自己被pk-1*p減掉了);

         phi(p1p2)=phi(p1)phi(p2) ,很容易證。

  因此能夠推出一個大結論:phi(n)= p1的k次方 * (11/p1)  *  p2的k次方 * (11p2)

                   =n*(11/p1)* (11p2);

用上素數篩的思想,若是p是x的因數,那麼phi(x*p)=phi(x)*p;

         若是p不是x的因數,那麼phi(x*p)=phi(x)*(p-1);

因而乎,能夠線性了:    

bool isprime[n]; int prime[n],tot=0; int phi[n]; memset(isprime,1,sizeof(isprime));//默認都是質數
    for(int i=2;i<=n;i++) { if(sprime[i]) { phi[i]=i-1; prime[tot++]=i; } for(int j=1; j<=tot && prime[j] * i<=n;j++) { isprime[i*prime[j]]=0; if(i%prime[j]==0){ phi[i*prime[j]]=phi[i]*prime[j];//重要結論1
                break; } else phi[i*prime[j]]=phi[i] * (prime[j]-1);//重要結論2
 } }

 

能夠看出,兩個函數篩都是基於素數篩的,都是維護了合數的最小質因子。

相關文章
相關標籤/搜索