質數篩
暴力篩
程式碼:
1 bool check_prime(int n) 2 { 3 if(n==1) return 0; 4 for(int i=1;i<=n;i++) 5 if(n%i==0) return 1; 6 return 0; 7 }
六篩法
程式碼:
1 bool check_prime(int n) 2 { 3 if(n==1) return 0;//1既不是質數也不是合數 4 if(n==2||n==3) return 1;//2,3不是6兩側的數但他是質數 5 if(n%6!=1&&n%6!=5) return 0;//質數只能是6兩側 6 int x=sqrt(n);//篩到根號n 7 for(int i=5;i<=x;i++) 8 if(n/i==0||n/(i+2)==0) return 0;//如果他還有約數 9 return 1;//若沒有約數 10 }
引理:算術基本定理——任何一個大於1的自然數N,如果N不為質數,那麼N可以唯一分解成有限個質數的乘積
所以只需要n無法被質數整除n即為質數
由於6兩側的數不是2和3的倍數所以不需要用2和3來篩
也不需要用6n+2,3,4來篩因為這些都是2和3的倍數,如果該數能被篩掉則改該數也能被2和3整除,不符合上述條件
線性篩 (也叫尤拉篩)
程式碼:
int primes[N], cnt; // primes[]儲存所有素數 bool st[N]; // st[x]儲存x是否被篩掉 void get_primes(int n) { for (int i = 2; i <= n; i ++ ) { if (!st[i]) primes[cnt ++ ] = i;//如果沒有沒有被篩掉就存入primes for (int j = 0; primes[j] <= n / i; j ++ ) { st[primes[j] * i] = true;if (i % primes[j] == 0) break; } } }
引理:算術基本定理——任何一個大於1的自然數N,如果N不為質數,那麼N可以唯一分解成有限個質數的乘積
顯然,在所有大於0的自然數中,除了質數就是合數.要求質數,只需要”篩”去所有的合數即可.如何篩去合數
這裡就應用到了算術基本定理.關於合數,這裡我們需要注意兩點:
(1).所有合數都要篩到
(2).不能有重複篩選,否則無法達到線上性時間內的運行了
要做到第一點,根據算術基本定理:n=q*m(q表示最小質數,m=N/q,顯然m>=q),對N內的m和q進行列舉
這樣我們就保證了能列舉N以內的所有整數,然而我們還不能保證列舉的合數不重複。
重點:先來思考一下為什麼會有重複,由於對於任意一個n=m*q,m和q是相對的兩個狀態,m大了q就變小,m小了q就變大。
重點:設n=m1*q1=m2*q2,q1<=q2<=m2<=m1我們這裡需要保證當且僅當在q1是最小質因數時能求解,也就是去除q2和m2的情況.
重點:因為q1與q2互質,且q1<q2,則有m2%q1=0;由於每一個i值都是不同的因此我們認為當前的i值符合m1的條件因此臨界條件為
重點:q1=q2,m2=m1,此時q1再增大時就有危險因為此時不符合除去q2和m2的條件
所以關鍵步驟來了: if(i%peimes[j]==0) break;
當i是primes[j]的倍數時直接跳出迴圈,設i=primes[j]*t,此時有i*primes[j+1]=primes[j]*t*primes[j+1]
可以看到此時的最小質數時primes[j],為了避免與當m=primes[j+1]*t時有重複,這裡可以通過break跳過計算
此外還需注意:要保證m從2開始,因為1*q=1且i%1==0;