在自然數集中,質數的數量不多而且分佈比較稀疏,對於一個整數N,不超過N的質數大概有N/lnN個,即每lnN個數中可能會有一個質數。
在篩選0—N中的素數的方法較多:
1.試除法
bool is_prime(int n) { for(int i=2;i<=sqrt(n);i++) if(n%i==0)return false; return true; }
2.Eratosthenes篩選法
想法是:任意整數x的倍數 2x,3x,..都不會是質數
我們從2開始掃,從小到大掃每個數x,把他的倍數2x,3x,...,[N/x]*x標記爲合數.
掃到一個數時,它不被2—x-1之間任何數整除,該數即爲質數
當然,我們會發現,2和3都會把6標記爲合數。
由此我們可知,小於x^2的x的倍數在掃更小的數時已經被掃了一遍。
就不必像2和3都會把6掃一遍那樣多餘的白白浪費運算時間了。
我們要做的就是對這個算法進行優化
:對於每個數x,我們只需從x^2開始掃,把x^2,(x+1)*x,...,[N.x]*x標記合數即可。
void primes(int n) { memset(v,0,sizeof(v));//標記合數 for(int i=2;i<=n;i++) { if(v[i])continue; else cout<<i<<endl;//看是不是質數,是質數的話輸出 for(int j=i;j<=n/i;j++)v[i*j]=1; } }
當然這篇blog到此不會截止,我想說的是:雖然.Eratosthenes篩選法是讓素數x從x^2往上開始2篩的,但還是會造成重複篩選。
就像2和3都會把6標記爲合數一樣,
雖然Eratosthenes篩選法是把x^2,(x+1)*x,...,[N.x]*x標記合數
但還是會造成重複篩選。
如:12=6*2,12=4*3,很明顯12被重複篩選了,
Eratosthenes篩選法 的本質和爆破的試除法 一樣,只不過減少了重複篩選的次數。
而我們想知道的是產生一個合數的唯一方式。
這時我們講一下線性篩:
舉個簡單的例子:我們通過 從小到大累積質因子 來標記每個合數,讓12=3*2*2是合數組成的唯一的方式
線性篩是通過 從小到大累積質因子 來標記每個合數,當我們理解這句話的含義時,實現代碼就不難了
int v[maxn],prime[maxn];//prime用來記載質數 void primes(int n) { memset(v,0,sizeof(v)); m=0;//最小質因子 for(int i=2;i<=n;i++) { if(v[i]==0) { v[i]=i;prime[++m]=i; }//i是質數的話 //讓當前數i乘上一個質因子 for( int j=1;j<=m;j++) { //i有比prime[j]更小的質因子 或超出n的範圍停止 if(prime[j]>v[i] ||prime[j]>n/i )break; //prime[j]是合數i*prime[j]的最小質因子 v[i*prime[j]]=prime[j]; } } for(int i=1;i<=m;i++)cout<<prime[i]<<endl; }
這種方法省時就省時在,不會造成重複篩選!
其實關於質數的題目還有一個應用就是:質因數分解
pi序列都爲質數,ci爲次冪
對任何N,都有:
N=p1^c1*p2^c2.......
由此可以利用 試除法 和 Eratosthenes篩選法 完成質因數分解:
其實 它的一個更好的應用是求最大質因子 因爲一個數字不可能有兩個大於根號的因子,還是素因子所以我們函數內,for循環的條件是:
for(int i=2;i<=sqrt(n);i++)
#include<iostream> #include<algorithm> #include<math.h> using namespace std; const int maxn=1e5; long long p[maxn]; long long c[maxn]; void divide(int n) { int m=0; for(int i=2;i<=sqrt(n);i++) { if(n%i==0)//i爲質數 { p[++m]=i;c[m]=0; while(n%i==0) { n/=i;c[m]++; } } } //最後剩餘的數不能爲任何區間內質數整除,則剩餘的數 也爲質數 if(n>1) {p[++m]=n;c[m]=1; } for(int i=1;i<=m;i++) cout<<p[i]<<" "<<c[i]<<endl; } int main() { int n;cin>>n; divide(n); return 0; }