線性素數篩也算是一種基本的演算法吧,和揹包一樣,必須要做到深入理解其原理,並且能快速打出**來。
兩個大前提:
1. 自然數中,1既不是素數也不是合數。(小學知識)
2. 乙個合數一定能分解成乙個素數和另乙個數相乘。(這個我不能證明,但是科學應該不會有錯)
先從普通的思路談起。
如果問你乙個數是不是素數,那你一定是for迴圈一下,看看有沒有數能夠整除它。
現在,如果我們已經知道乙個數是素數了,那麼這個數的k倍(k>=2),一定是合數。(這個無序任何證明)
所以乙個很簡單的想法就是每找到乙個素數,我們就把它的k倍(k>=2)都標記為合數。所以我們需要開乙個陣列記錄有哪些素數。
這裡我們用prime陣列來記錄素數。用bool型別 isprime來記錄乙個數到底是不是素數。
按這個思路,我們可以先寫一下初步的**。
int prime[maxn],total;
bool isprime[maxn];
void first(int size){
memset(isprime,1,sizeof(isprime));//注意這裡isprime是布林型別,乙個位元組,所以我可以賦值為1,如果是int型別,則不能賦值為1。
isprime[1]=false;//1不是素數.
for(int i=2;i但是你會發現複雜度不是o(n)。所以這裡還需要一些優化。
我們來看乙個很簡單的例子:
當i=9時,當前已有的素數有2,3,5,7。
此時我將進入二重迴圈,一次將 2*9=18,3*9=27,5*9=45,7*9=63 標記為合數。
但是有個問題,5*9 等價於3*15,也就是說當i=15時,也會標記一次。這明顯重複了。同理,7*9等價於3*21,當i=21時,也會重複標記一次。
多觀察幾組資料後,你會發現,其實只要找到i%prime[j]==0時,我們就可以跳出迴圈了,因為後面的素數的倍數,我們可以留到後面進行標記。那麼這樣的猜想到底對不對?下面看乙個結論和證明。
假設 a,d為合數,b,c為素數 其中b>c.
a= b*c*d
令e=c*d(顯然e也是合數)
令f=d*b(也是合數)
a=f*c (f是合數,c是素數)。對比第乙個等式,a=b*c*d 。我們發現a可以有這兩種不同的表示方法,不同的是我們可以用乙個較小的素數*乙個較大合數 來代替 乙個較大素數*乙個較小合數(a=b*(c*d))。
所以有下面的結論:
乙個比合數大的素數和該合數的乘積可以用乙個更大的合數和比其小的素數相乘得到。
那麼如何優雅的在**裡運用這個結論呢?
答案就是只要找到乙個i%prime[j]==0 我們就跳出迴圈,因為後面總有更大的合數和乙個小的素數相乘,滿足我們需要標記的數。
最終**:
void getprime(int size)
{ memset(isprime,1,sizeof(isprime));
isprime[1]=false;
for(int i=2;i記在腦子裡的才是自己的,模板都是虛假的。
深入理解線性質數 素數 篩
int p n cnt p n 用來存質數,cnt表示質數的個數 bool st n 表示當前的數有沒有被篩過樸素篩法 o n logn o nlogn o nlog n void get primes int n 由於當執行到 i 時可以確保已經用小於 i 的每乙個質數都篩過一遍,因此可以保證每乙...
素數篩法(素數篩 線性篩)
求素數的方法在現階段可以總結為三種 這種方法最為簡單但效率太低,經過優化時間複雜度最低是o n sqrt n 輸入乙個n,輸出n以內所有素數 include intprime int n if flag 0 優化 printf d i intmain 素數篩法原理 2是素數,那麼2的所有倍數都是合數...
素數線性篩
ps 證明 神牛部落格。include include using namespace std const int n 100100 int v n p n n,m,tot int main 首先,先明確乙個條件,任何合數都能表示成一系列素數的積。不管 i 是否是素數,都會執行到 關鍵處1 如果 i...