使用線性篩以及素數篩求出某範圍內的所有素數

2021-10-01 20:49:20 字數 3464 閱讀 8484

我們規定:若乙個數字的因數只有1和它本身,那麼這個數就是素數(1除外,所以最小的素數是2)

1 暴力求解

#include

#include

using

namespace std;

#define max_n 1000

//表示求1000以內(包含1000)的所有素數

vector<

int> prime;

//用於儲存所有的素數

bool

judge

(int n)

return

true;}

intmain()

} cout <<

"素數個數為:"

<< cnt << endl;

//將prime中的素數輸出

vector<

int>

::iterator it;

int tmp =0;

for(it = prime.

begin()

; it != prime.

end(

); it++

)return0;

}

注:甚至還有小夥伴使用更加暴力的做法,也就是在judge()函式中,for迴圈從2到n,而不是從2到n

\sqrt

n​。其實這樣是完全沒有必要的。

我們假設: n = a * b (假設a < b),那麼此時一定有:a <= n

\sqrt

n​並且 b => n

\sqrt

n​,否則的話,若a b均小於或者a b均大於n

\sqrt

n​,那麼他們的乘積一定不等於n。因此若for迴圈從2到n

\sqrt

n​此時還沒有乙個數,能夠使 n % i == 0 成立,那n肯定是素數,從n

\sqrt

n​到n這部分數字完全是沒有必要判斷。

2 使用素數篩

2.1 素數篩的基本思想

若某乙個數字為素數,那麼該數字的整數倍(1倍除外)肯定是合數。

為此我們引入乙個陣列 prime[max_n + 5] = ,其中max_n表示求解的是max_n以內的所有素數,陣列初始化為0。並且我們規定若 prime[i] == 0,則 i 是素數,否則 i 為合數。

2.2 對應**如下

#include

using

namespace std;

#define max_n 1000

int prime[max_n +5]

=;void

init()

}//為了後面操作方便,我們從prime[1]向後依次存放找到的素數

for(

int i =

2; i <= max_n; i++)}

intmain()

return0;

}

注:本實驗**的核心就是init()函式中的兩個for迴圈巢狀。總的來說我們就是要把素數的整數倍(1倍除外)標記為1。但是細心的小夥伴會問,這樣的話,裡層for迴圈 j 應該從2∗i

2 * i

2∗i開始呀,為什麼是 i 的平方開始呢?

舉個例子吧,假設現在 i = 5,prime[5] = 0,因此我們進入裡層迴圈,如果 j 從 2∗i

2 * i

2∗i開始,也就是 2 * 5開始,標記prime[10] = 1。這樣確實可以,但是在prime[2] = 0的時候,也就是2為素數,此時我們已經把所有2的倍數全部置成1了,也就是說prime[10]早已經被標記為1。此時若再標記,不就是重複操作了嗎!!!

2.3 素數篩演算法的時間以及空間複雜度

空間複雜度: o(n)

時間複雜度: o(n * log(log(n))) //時間複雜度不太確定,回頭我再看看

3 使用線性篩

3.1 素數篩的不足

舉個例子,對於數字30而言,在素數篩中它肯定被標記為1,但是它被標記為1的次數,有幾次?,僅僅只有一次嗎? 答案顯然不是!!

數字30 被2標記了一次,被3標記了一次,被5標記了一次,也就是說30這個數字一共被標記了3次。

「不對不對」,可能會有小夥伴問,你之前在素數篩中不是存在過重複標記嗎?當時你不是把內層迴圈從i * i 開始,減少了重複標記呀!!

對,但這只是減少標記,我們可以順著程式流程走一遍:2是素數,所以我們從4開始,把所有2的倍數全部標記為1;3是素數,所以我們從9開始,把所有3的倍數全部標記為1;5也是素數,因此我們從25開始,把所有5的倍數全部置為1。顯然在內層迴圈中,30被標記了3次。

所以有沒有更好的方法,可以每乙個數字只會被標記一次呢? 答案就是: 線性篩 !

3.2 線性篩的基本思想

在素數篩中我們根據素數來標記素數的整數倍,但線性篩中我們使用其他的數。

我們使用整數m,來標識整數n,此時m和n存在如下關係:

設 p 是 n 的最小質因數

n = p * m

p 比 數字m的最小質因數還要小

此時我們可以標記 m * p』 (p』是小於p的所有素數的集合中的元素) 位置上的數字

為了徹底明白,大家可以手寫模擬2到30的標記過程,若其中有乙個數字被標記了兩次或以上,那麼肯定有**出錯了,自己再回頭看看基本思想。

3.3 線性篩**實現

#include

using

namespace std;

#define max_n 1000

int prime[max_n +5]

=;void

init()

}}intmain()

return0;

}

注:**實現時,用原陣列從prime陣列1號位開始向後儲存素數, prime[0]儲存當前素數的個數。程式會有點難理解 (畢竟自己是過來人,知道不容易),但還請仔細研讀。

3.3 線性篩演算法的時間以及空間複雜度

線性篩演算法絕不僅僅只用來求解素數問題,它更多的是為用作一種演算法框架,在許多其他的演算法中也會用到,切記!!!!!!!!

這是自己第一次在csdn上寫部落格,雖然自己之前用過markdown格式編輯器(typora),但是兩者之間的操作還是存在區別的,不過還好,不會的可以上網搜尋,畢竟是自己的**作,值得留念!

貶低自己的話就不想再說了,知道自己的不足,才會邁向成功。自己也不想立flag,只想說:「不求自己一定要有多麼的努力,但求自己可以一步乙個腳印,每一天都在進步」,加油,路漫漫其修遠兮,吾將上下而求索,與君共勉!

素數篩法(素數篩 線性篩)

求素數的方法在現階段可以總結為三種 這種方法最為簡單但效率太低,經過優化時間複雜度最低是o n sqrt n 輸入乙個n,輸出n以內所有素數 include intprime int n if flag 0 優化 printf d i intmain 素數篩法原理 2是素數,那麼2的所有倍數都是合數...

素數篩和線性篩

素數篩就是標記所求範圍內的數字是否是合數 沒有被標記的為合數 具體步驟 用乙個陣列 a i 標記 i 是否是合數,是 a i 為 1 否則 a i 為 0 若 i 為素數則在所求範圍內的 i 的整數倍 a k i 全標記為 1 如 第一次標記到 2 為素數,則 2 的整數倍 4,6,8 均標記為合數...

素數線性篩

ps 證明 神牛部落格。include include using namespace std const int n 100100 int v n p n n,m,tot int main 首先,先明確乙個條件,任何合數都能表示成一系列素數的積。不管 i 是否是素數,都會執行到 關鍵處1 如果 i...