素數篩(埃氏篩法與尤拉篩)

Kevin_Matrix發表於2021-08-13

素數篩,其實是將一堆數中的合數給篩掉,留下素數的一個過程。求某個大小範圍內的素數個數,是用到素數篩的最最基礎的問題。
首先要給出關於素數的最基本的知識:判斷單個數是否為素數。


判斷一個整數n是否為素數

首先i從2開始列舉到 \(\sqrt{n}\) ,然後一旦n可以被i整除,就返回false,然後如果i列舉完了,n都沒能整除i,那就證明是素數,返回true。

bool isprime(int n){   //判斷單個數n是否為素數
	if(n <= 1) return 0;
	for(int i=2;i*i<=n;i++)
		if(n % i == 0) return 0;
	return 1;
}

埃氏篩法

作用就是列舉n以內的素數

首先將2到n範圍內所有的整數寫下來,在這其中最小的數字2是素數,將表中所有2的倍數都劃去,表中剩餘最小的數字是3,它的因數只有它自己,(它已經最小了),然後就再把表中所有3的倍數都劃去,以此類推,每次表中剩餘的最小數字是m,m就是素數,然後將表中所有m的倍數都劃去,這樣反覆操作之後就能依次列舉n以內的素數。

模板(數字n的作用就是篩出從1-n的素數,prime陣列存這些素數,isprime[ i ]如果為1,則i是素數):

int prime[n],isprime[n];

int eratosthenes(int n){
	int p = 0;
	for(int i=0;i<=n;i++)
		isprime[i] = 1;//先假設這些數都是素數,後面再由它們的因數來劃掉。
	isprime[0] = isprime[1] = 0;
	for(int i=2;i<=n;i++){
		if(isprime[i]){
			prime[p++] = i;
			for(int j = 2 * i;j <= n;j += i)
				isprime[j] = 0;
		}
	}
	return p;
}

埃氏篩法有一個缺陷,每次都要去對當前表內的素數m的所有的倍數進行刪掉,也就是“visit[ j ] = 0” 這個操作,對於一個合數,它很有可能會被很多個素數篩到,也就重複進行了很多次操作,在此基礎之上再次進行改進,我們還有尤拉篩法:


尤拉篩法

尤拉篩法並不是用目前最小素數的所有倍數來篩合數,而是要篩去一個合數時,用這個合數的最小質因子來篩去它。

尤拉篩法建立了一個陣列prime,然後它的下標從1開始依次存的是這個區間的從小到大的素數,然後在外層迴圈for中讓i累加,這裡的i其實就是“倍數”的含義,然後迴圈的時候,取出陣列中所有數,然後從小到大地對於i * 這些合數得到的值篩掉(它們都是合數)然後呢,i一直在增加,每一次執行這個篩操作之前,我們都要先判斷一下i是否為素數,若是的話就把i加入前面的陣列prime之中。

在這個篩操作中,還有一個continue操作的條件需要注意:if (i % prime[j] == 0) break;

這裡跳出本次迴圈直接進行下一次迴圈的意思是:當i是prime[ j ]的倍數時,i = k * prime[ j ] 如果進行下次迴圈,j 會加一指到prime陣列中的下一個素數,下一次迴圈中 會出現 i * prime[ j+1 ] 而因為i是prime[ j ] 的倍數,所以上式變為 prime[j] * k * prime[j+1] ,這個表示的是一個合數,而這個合數的最小素因子其實是prime[ j ] ,而不是 prime[j+1] 。也就是說這個時候要篩掉的這個數在前面已經被篩掉了,因為它有一個更小的素因子,所以這已經是一次重複的篩運算了。然後接下來的j遞增的內層迴圈中,i 一直是不變的,所以後面所有的i * prime[ j ] 都是會重複的,故至此跳出本次i的迴圈,直接進行下一次i的迴圈了。

下面prime[0] 表示有多少個素數,然後從陣列的下標1開始存第一個素數,以此類推

程式碼:

const int maxn = 100;
int prime[maxn];
int vis[maxn];
void eularsieve(){
	memset(vis,0,maxn * sizeof(int));//是0表示i是素數
	memset(prime,0,maxn * sizeof(int));
	for(int i=2;i<maxn;i++){
		if(!vis[i])
			prime[++prime[0]] = i;
		for(int j=1;j <= prime[0] && i * prime[j] <= maxn;j++){
			vis[i*prime[j]] = 1;
			if(i % prime[j] == 0)
				break;
		}
	}
}

相關文章