線性篩素數 探索中的不斷優化

2021-09-10 06:40:19 字數 2735 閱讀 5929

工欲善其事必先利其器

首先素數是什麼?

素數就是乙個數除了1和他本身沒有其他因數的數叫做質數。

合數即為對立概念

當然,1既不是素數也不是合數

素因子是什麼?

由尤拉函式得到結論:

每乙個合數都可以寫成幾個素數相乘的形式,

這些素數即為該合數的質因子

我們的目的是建立一張素數表

範圍可達1~1e8左右

以bool陣列存放,是素數為true 否則為false

基於這些數學定義,有以下演算法

素數的高效判斷方法

v1.0 全遍歷時間複雜度為o(n)

bool is_prime_1(int n) 

}return true;

}

v2.0根號優化時間複雜度為o(sqrt(n))
bool is_prime_2(int n) 

}return true;

}

v2.5位運算優化時間複雜度為o(sqrt(n)/2)

思路超級簡單易懂

n%2=0數必不為素數

以二進位制存放的數最後1bit一定是0,n&1=0

或者直接n%2==0特判

bool is_prime_2.5(int n) 

}return true;

}

v3.0 %6篩時間複雜度為o(sqrt(n)/3)

這個方法有點genius

證明:令x≥1,將大於等於5的自然數表示如下:······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ······可以看到,不在6的倍數兩側,即6x兩側的數為6x+2,6x+3,6x+4,由於2(3x+1),3(2x+1),2(3x+2),所以它們一定不是素數,再除去6x本身,顯然,素數要出現只可能出現在6x的相鄰兩側。這裡有個題外話,關於孿生素數,有興趣的道友可以再另行了解一下,由於與我們主題無關,暫且跳過。這裡要注意的一點是,在6的倍數相鄰兩側並不是一定就是質數。此時判斷質數可以6個為單元快進,即將方法(2)迴圈中i++步長加大為6,加快判斷速度,原因是,假如要判定的數為n,則n必定是6x-1或6x+1的形式,對於迴圈中6i-1,6i,6i+1,6i+2,6i+3,6i+4,其中如果n能被6i,6i+2,6i+4整除,則n至少得是乙個偶數,但是6x-1或6x+1的形式明顯是乙個奇數,故不成立;另外,如果n能被6i+3整除,則n至少能被3整除,但是6x能被3整除,故6x-1或6x+1(即n)不可能被3整除,故不成立。綜上,迴圈中只需要考慮6i-1和6i+1的情況,即迴圈的步長可以定為6

bool is_prime3.0(int n)

v4.0埃拉託斯特尼篩法(埃氏篩)o(nloglogn)

接近線性但不是

基本思想:找到乙個素數,不斷倍增,得到的數一定不是素數,篩去。

#includeusing namespace std;

#define max 10000000

bool prime[max];

void seek_prime()

cout<這裡有乙個小優化,j 從 i * i 而不是從 i + i開始,因為 i(2~ i-1)在 2~i-1時都已經被篩去,所以從i * i開始。

上面的小程功能是找出1~n素數個數

v5.0尤拉線性篩 o(n)

埃氏篩的缺點很明顯 :對於乙個合數,有可能被篩多次。例如 30 = 215 = 310 = 5*6……那麼如何確保每個合數只被篩選一次呢?我們只要用它的最小質因子來篩選即可

先看**後解釋

/*求小於等於n的素數的個數*/

#include#includeusing namespace std;

int main()

{ int n, cnt = 0;

int prime[100001];//存素數

bool vis[100001];

scanf("%d", &n);

memset(vis, false, sizeof(vis));//初始化假設全為素數

memset(prime, 0, sizeof(prime));

for(int i = 2; i <= n; i++)

{if(!vis[i])//不是目前找到的素數的倍數

prime[cnt++] = i;//找到素數~

for(int j = 0; j其他都還好

難理解步驟:i % prime[j] == 0,這也是該篩法的優越之處

當 i是prime[j]的倍數時

i一定會被之前prime[j]篩過

證:i = kprime[j] (可證明kprime[j+1] = prime[j]kprime[j+1],這裡prime[j]是最小的素因子,當i = kprime[j+1]時會重複篩,所以才跳出迴圈。

舉個例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出迴圈,prime[j+1] = 3,83 = 243 = 2*12,被之前prime[j],i=12篩。因為尤拉篩法的原理便是通過最小素因子來消除。

線性篩素數 尤拉篩 字首和優化

關於素數的定義 在大於1的自然數中,除了1和它本身以外不再有其他因數。判斷乙個數是否是素數 int x 要求的數 for int i 2 i sqrt x i 埃氏篩法 時間複雜度 o nloglogn int num prime 0 素數的數量 int prime 5005 放素數 int che...

素數的線性篩 尤拉函式

o n 篩選素數 includeusing namespace std const int m 1e6 10 int mindiv m 每個數的最小質因數 int prim m pnum 存素數 bool vis m void prim for int j 0 j pnum j int main 尤...

線性素數篩(尤拉篩)(超級好的MuBan)

problem 找出小於等於n的所有素數的個數。include using namespace std const int maxn 1e6 int prime maxn 尤拉線性素數篩,o n bool vis maxn 標記 int prime int n return cnt int main...