1. 程式人生 > 其它 >質數篩法

質數篩法

基本定義

質數,也稱素數,是指在大於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;
			}
	    }
	}
}