質數篩法
阿新 • • 發佈:2021-08-13
基本定義
質數,也稱素數,是指在大於1的自然數中,除了1和它本身以外不再有其他因數的自然數。
一、試除法
簡單而暴力,檢視\(i(2\leq i\leq N)\)是否整除\(n,\)時間複雜度為\(O(sqrt(n)),\)相對來說比較低效
bool is_prime(int n) {
if (n < 2) return false;
for(int i = 2; i <= sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
二、埃式篩法
全稱埃拉託斯特尼篩法\((Eratosthenes篩法)\)
合數可以表示成一個自然數和一個素數的乘積,因此我們找到一個素數後,我們要做的就是把他小於\(n\)的倍數全部標記為合數。時間複雜度為\(O(nloglogn),\)許多合數被無意義地標記了許多次,降低了效率。
該演算法實現簡單,效率已經非常接近於線性,是演算法競賽中最常用的質數篩法。
int Eratosthenes(int n) { int w = 0; for (int i = 0; i <= n; i++) is_prime[i] = 1; is_prime[0] = is_prime[1] = 0; for (int i = 2; i <= n; i++) if (is_prime[i]) { prime[w++] = i; //2到i - 1的倍數我們之前篩過了,直接從i的倍數開始,提高了執行速度 for (int j = i * i; j <= n; j += i) is_prime[j] = 0; } return w; }
三、尤拉篩法
能夠讓所有合數只被標記一次,時間複雜度被降到\(O(n)\)。
同時,可以利用尤拉篩法順帶求出尤拉函式。
void init() { phi[1] = 1; for (int i = 2; i < MAXN; ++i) { if (!vis[i]) { phi[i] = i - 1; pri[cnt++] = i; } for (int j = 0; j < cnt; ++j) { if (1ll * i * pri[j] >= MAXN) break; // 關鍵地方,保證了每個數最多被篩一次,將時間複雜度降到了線性。 vis[i * pri[j]] = 1; if (i % pri[j]) phi[i * pri[j]] = phi[i] * (pri[j] - 1); else { // i 之前被 pri[j] 篩過了 // i 乘上其他的質數的結果一定會被 pri[j] 的倍數篩掉,所以這裡直接 break 掉就好了 phi[i * pri[j]] = phi[i] * pri[j]; break; } } } }