線性篩法


線性篩法

Eratosthenes 篩法利用的原理是 任意整數 x 的倍數 2x,3x,... 等都不是質數

但是即便如此也會有重復標記的現象,例如12既會被2又會被3標記,在標記2的倍數時,\(12 = 6*2\),在標記3的倍數時,\(12 = 4*3\) ,根本原因是沒有找到唯一產生12的方式。

線性篩法的核心原理

每個合數必有一個最大因子(不包括它本身),用這個因子把合數篩掉

換言之:每個合數必有一個最小素因子,用這個因數把合數篩掉

過程

假設對於一個確定的整數 \(i\)\(i\) 是一個合數 \(t\) 的最大因數,\(t\) 顯然可能不唯一(例如 30 和 45 最大因數都是 15)。但是仔細想一想,必然有一個p,滿足:

\[t = i * p ~~ (p\le i ,p是質數) \]

  • \(p\)為什么一定小於等於 \(i\)?因為 \(i\)\(t\) 的最大因數。
  • 為什么 \(p\) 一定是質數?因為如果 \(p\) 是合數,那么 \(i\) 就一定不是 \(t\) 的最大因數,因為 \(p\) 可以再拆成若干素數相乘,這些素數再與 \(i\) 相乘會使該因數更大。

既然如此,我們只需要把所有小於 \(i\) 的質數 \(p\) 都挨個乘一次拿到所有合數就好了。可是,這樣就不會有重復標記嘛?

會的,我們一不小心就忘記了最初的條件。我們要滿足 \(i\)\(t\) 的最大因數。如果 \(p\) 大於 \(i\) 的最小質因數,那 \(i\) 還是 \(t\) 的最大因數嘛?顯然不是,任何一個合數 \(t\) 都能唯一分解為有限個質數的乘積,除去這其中最小的質因數,其他的都乘起來就是最大因數 \(i\) 。所以我們不能讓 \(p\) 大於 \(i\) 的最小質因數(設為\(x\)),否則 \(i\) 將不再是 \(t = i*p\) 的最大因數,其最大因數應該是\(i*p/x\)

下面有兩個版本,核心處理稍有一點點不同,理解即可。

版本一

  • v[i] 表示 i 的最小質因數。如果i就是質數,那么v[i] = i
  • prime[j] 表示第 j 個質數。與之前的篩法不同,這個數組是存放質數的,而不是標記質數的
#define MAXN 1000012
int prime[MAXN],v[MAXN];
int m=0;//m表示現在篩出m個質數
void primes(int n)
{
	for(int i=2;i<=n;i++)
    {
		if(v[i]==0)//如果v[i]為0,說明 i 之前沒有被篩到過,i 為質數
		{
			v[i] = i;
             prime[++m] = i;
		}
		for(int j = 1;j<=m;j++)//遍歷小於 i 的所有質數
		{
            //如果質數大於 i 的最小質因數或者乘起來大於n就跳出循環
			if(prime[j] > v[i] || prime[j] > n/i) break;
			v[i*prime[j]] = prime[j];//標記 i*prime[j] 的最小質因數是prime[j]
		}
	}
}

版本二

  • v[i] i 為質數則為0,否則為 1
  • prime[j] 與上面相同
#define MAXN 1000000
int prime[MAXN],v[MAXN];
int m=0;//m表示現在篩出m個質數
void primes(int n)
{
    v[1] = 1;//1不是質數,提前處理
	for(int i=2;i<=n;i++)
    {
		if(v[i]==0)//如果v[i]為0,說明 i 之前沒有被篩到過,i 為質數
             prime[++m] = i;
		for(int j = 1;j<=m;j++)//遍歷小於 i 的所有質數
		{
            //乘起來大於就跳出循環
			if(prime[j] > n/i) break;
			v[i*prime[j]] = 1;//標記 i*prime[j] 的最小質因數是prime[j]
             //當遇到最小的質數是i的因數時,break
             if(i%prime[j]==0)break;
		}
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM