素數篩法
阿新 • • 發佈:2020-12-06
入土
初識c++的素數篩舉,n2暴力,列舉每一個數,如果這個數的約數只有兩個,它就為質數
入門
n2時,只判斷到根號就剎車
經典
埃式篩法
O(nloglogn)
基本思想:從2開始,將每個質數的倍數都標記成合數,以達到篩選素數的目的
node
int vis[maxn]; void Prime(){ memset(vis,0); //初始化都是素數 vis[0] = vis[1] = 1; //0 和 1不是素數 for (int i = 2; i <= maxn; i++) { if (!vis[i]) { //如果i是素數,讓i的所有倍數都不是素數 for (int j = i*i; j <= maxn; j += i) { vis[j] = 1; } } }
缺陷:
對於一個合數,有可能被篩多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……
那麼如何確保每個合數只被篩選一次
進階
尤拉篩
O(n)
基本思想:在埃氏篩法的基礎上,讓每個合數只被它的最小質因子篩選一次,以達到不重複的目的
node
int prime[maxn]; int visit[maxn]; void Prime(){ memset(visit,0); memset(prime, 0); for (int i = 2;i <= maxn; i++) { if (!visit[i]) { prime[++prime[0]] = i; //紀錄素數, 這個prime[0] 相當於 cnt,用來計數 } for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) { visit[i*prime[j]] = 1; if (i % prime[j] == 0) { break; } } } }
1.對於visit[i*prime[j]] = 1 的解釋:
這裡不是用 i 的倍數來消去合數,而是把 prime裡面紀錄的素數,升序來當做要消去合數的最小素因子
用 i 來當倍數
2.對於 i % prime[j] == 0 就break的解釋 :
當 i 是 prime[j]的倍數時,i = k * prime[j],如果繼續運算 j+1,i * prime[j+1] = prime[j] * k * prime[j+1],
這裡prime[j]是最小的素因子,當i = k * prime[j+1]時會重複,所以才跳出迴圈
舉個例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出迴圈,prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12,
在i = 12時會計算,因為尤拉篩法的原理便是通過最小素因子來消除。