暴力版:數組
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次方 * (1−1/p1) * p2的k次方 * (1−1p2)
=n*(1−1/p1)* (1−1p2);
用上素數篩的思想,若是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 } }
能夠看出,兩個函數篩都是基於素數篩的,都是維護了合數的最小質因子。