標籤:prim 質數 cpp 分解 遍曆 code 重複 線性篩 因子
線性篩法
上節課講的是 Eratosthenes 篩法利用的原理是 任意整數 x 的倍數 2x,3x,... 等都不是質數 。
但是即便如此也會有重複標記的現象,例如12既會被2又會被3標記,在標記2的倍數時,\(12 = 6*2\),在標記3的倍數時,\(12 = 4*3\) ,根本原因是沒有找到唯一產生12的方式。
線性篩法的核心原理
每個合數必有一個最大因子(不包括它本身),用這個因子把合數篩掉
換言之:每個合數必有一個最小素因子,用這個因數把合數篩掉
過程
假設 i 是合數 t 的最大因數,t顯然可能不唯一(例如 30 和 45 最大因數都是 15)。但是仔細想一想,必然有
t = i * p(p為小於 i 的質數) 。
- p為什麼比 i 小?因為 i 是 t 的最大因數。
- 為什麼 p 一定是質數?因為如果 p 是合數,那麼 i 就一定不是 t 的最大因數,因為 p 可以再拆成若干素數相乘,這些素數再與 i 相乘會使因數更大。
既然如此,我們只需要把所有小於 i 的質數 p 都挨個乘一次好了。
真相真的是這樣的嘛?
其實不是的,一不小心就忘記了最初的條件。我們要滿足 i 是 t 的最大因數。如果 p 大於 i 的最小質因數,那 i 還是 t 的最大因數嘛?顯然不是,任何一個合數都能唯一分解為有限個質數的乘積,除去這其中最小的質因數,其他的都乘起來就是最大因數 i 。所以我們不能讓 p 大於 i 的最小質因數。
v[i] 表示 i 的最小質因數。規定如果為 1,v就是質數。
prime[j] 表示第 j 個質數。與之前的篩法不同,這個數組是存放質數的,而不是標記質數的
#define MAXN 1000000int prime[MAXN],v[MAXN];int m=0;//m表示現在篩出m個質數void primes(){ v[1] = 0; for(int i=2;i<MAXN;i++) { if(v[i]==0)//如果v[i]為0,說明 i 之前沒有被篩到過,i 為質數 { v[i] = 1; prime[++m] = i; } for(int j = 1;j<=m;j++)//遍曆小於 i 的所有質數 { //如果質數大於 i 的最小質因數或者乘起來大於MAXN就跳出迴圈 if(prime[j] > v[i] || prime[j] > MAXN/i) break; v[i*prime[j]] = prime[j];//標記 i*prime[j] 的最小質因數是prime[j] } }}
線性篩法