埃氏篩與歐拉篩

1.埃氏篩
思想:任意合數都可以表示成幾個素數的乘積,那麼我們可以每找到一個素數,就將他的倍數都標記(代表這個數是合數)。2是最小的素數,所以我們從2開始標記。時間複雜度爲:O(n*logn)
代碼如下:

const int maxn=1e5+10;
bool flag[maxn];
int prime[maxn];
int pri_cnt=0;
void get_prime(int n){
	for(int i=2;i<=n;i++){
		if(!flag[i]){
			prime[++pri_cnt]=i;
			for(int j=i*i;j<=n;j+=i){
				flag[j]=1;
			}
		}
	}
}

  對於上面j=i*i可能會有人有疑問,按照上面的思路,初始值j不應該等於2*i嗎,因爲區間i*(2~ i-1)2~i-1時都已經被篩去,所以從i * i開始.
2.歐拉篩
思想:由埃氏篩我們已經知道篩法的核心就是標記,但是我們可以發現:即便我們做了一點優化,隨着數據量的增長,一些擁有多個素因子的合數會被重複標記。我們可以想到用唯一素數來標記合數,這樣每個合數都只會被標記一次,因此時間複雜度降到O(n);
代碼如下:

const int maxn=1e5+10;
int v[maxn],prime[maxn];//數組v記錄每個數的最小質因子 
int pri_cnt=0;//pri_cnt記錄質數的個數 
void prime_table(int n){
	for(int i=2;i<=n;i++){
		if(v[i]==0){//i是質數 
			v[i]=i;	
			prime[++pri_cnt]=i;
		}
		for(int j=1;j<=pri_cnt;j++){
			//如果(i*prime[j])有比prime[j]更小的因子,或者超出n的範圍,停止循環
			if(prime[j]>v[i] || prime[j]>(n/i)) break;
			//prime[j]是合數i*prime[j]的最小因子 
			v[i*prime[j]]=prime[j];
		} 
	} 
}

  prime[j]>v[i]表示當前的素數prime[j]比累乘因子i的最小素因子大,這就代表合數i*prime[j]應該被i的最小素因子v[i]篩掉,而不是被當前的素數prime[j]篩掉,舉個例子:12=4x3;當i=4 prime[j]=3時,12本應該被2篩掉,也就是被4的最小素因子2(v[4]=2)篩掉,輪不到3來篩,所以跳出循環。
  prime[j]>(n/i)prime[j]*i>n的意思,用除法不用乘法是爲了防止溢出(當數據量很大的時候,會爆int)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章