我們規定:若乙個數字的因數只有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∗i2 * 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...