數論二 Eular質數篩法

2021-07-24 15:04:40 字數 2758 閱讀 6026

如何去快速得求解[1,n]這個區間內素數的個數呢?

自然我們已經知道了如何快速判定乙個數是否是質數,那麼我就直接將[1,n]之間每乙個數判定一次,就可以得到結果。

雖然我們已經通過快速素數檢測將每一次判定的時間複雜度降低,但是n個數字的話,總的時間複雜度依舊很高。

發現如果乙個數p是質數的話,那麼它的倍數一定都不是質數。所以我建立了乙個布林型別的陣列isprime,初始化都為true。我從2開始列舉,當我找到乙個isprime[p]仍然為true時,可以確定p一定是乙個質數。接著我再將n以內所有p的倍數全部設定為isprime[p*i]=false。

寫成偽**為:

isprime = true

primecount = 0

for i = 2 .. n

if isprime[i] then

primecount = primecount + 1

multiple = 2

while (i * multiple ≤ n)

isprime[i * multiple] = false

multiple = multiple + 1

end while

end if

end for

這個演算法叫做eratosthenes篩法,是一種非常古老的質數篩選演算法。其時間複雜度為o(n log log n)。但是這個演算法有乙個冗餘的地方:比如合數10,在列舉2的時候我們判定了一次,在列舉5的時候我們又判定了一次。因此使得其時間複雜度比o(n)要高。

乙個改進的方法叫做eular篩法,其時間複雜度是o(n)的。

輸入第1行:1個正整數n,表示數字的個數,2≤n≤1,000,000。

輸出第1行:1個整數,表示從1到n中質數的個數

樣例輸入

9

樣例輸出

我們可以知道,任意乙個正整數k,若k≥2,則k可以表示成若干個質數相乘的形式。eratosthenes篩法中,在列舉k的每乙個質因子時,我們都計算了一次k,從而造成了冗餘。因此在改進演算法中,只利用k的最小質因子去計算一次k。

首先讓我們了解一下eular篩法,其偽**為:

isprime = true

primelist =

primecount = 0

for i = 2 .. n

if isprime[i] then

primecount = primecount + 1

primelist[ primecount ] = i

end if

for j = 1 .. primecount

if (i * primelist[j] > n) then

break

end if

isprime[ i * primelist[j] ] = false

if (i % primelist[j] == 0) then

break

end if

end if

end for

與eratosthenes篩法不同的是,對於外層列舉i,無論i是質數,還是是合數,我們都會用i的倍數去篩。但在列舉的時候,我們只列舉i的質數倍。比如2i,3i,5i,...,而不去列舉4i,6i...,原因我們後面會講到。

此外,在從小到大依次列舉質數p來計算i的倍數時,我們還需要檢查i是否能夠整除p。若i能夠整除p,則停止列舉。

利用該演算法,可以保證每個合數只會被列舉到一次。我們可以證明如下命題:

假設乙個合數k=m*p1,p1為其最小的質因子。則k只會在i=m,primelist[j]=p1時被篩掉一次。

首先會在i=m,primelist[j]=p1時被篩掉是顯然的。因為p1是k的最小質因子,所以i=m的所有質因子也≥p1。於是j迴圈在列舉到primelist[j]=p1前不會break,從而一定會在i=m,primelist[j]=p1時被篩掉

其次不會在其他時候被篩掉。否則假設k在i=n, primelist[j]=p1時被篩掉了,此時有k=n*p2。由於p1是k最小的質因子,所以p2 > p1,m > n且p|n。則i=n,j列舉到primelist[j]=p1時(沒到primelist[j]=p2)就break了。所以不會有其他時候篩掉k。

同時,不列舉合數倍數的原因也在此:對於乙個合數k=m*2*3。只有在列舉到i=m*3時,才會計算到k。若我們列舉合數倍數,則可能會在i=m時,通過m*6計算到k,這樣也就造成了多次重複計算了。

綜上,eular篩法可以保證每個合數只會被列舉到一次,時間複雜度為o(n)。當n越大時,其相對於eratosthenes篩法的優勢也就越明顯。

#include #include #include #include #include #include #include #define ll long long

using namespace std;

//file *stream;

int n;

const int n = 1000005;

bool isprime[n];

int primelist[n];

int primecount = 0;

int count[n];

void eular()

count[i] = primecount;

for (int j = 1; j <= primecount; ++j)

/*int multiple = 2;

while (i*multiple <= n)

*/ }}

int main()

數論二 Eular質數篩法

時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述小ho 小hi,上次我學會了如何檢測乙個數是否是質數。於是我又有了乙個新的問題,我如何去快速得求解 1,n 這個區間內素數的個數呢?小hi 你自己有什麼想法麼?小ho 有!我一開始的想法是,自然我們已經知道了如何快速判定乙...

hihocoder 數論二 Eular質數篩法

時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述小ho 小hi,上次我學會了如何檢測乙個數是否是質數。於是我又有了乙個新的問題,我如何去快速得求解 1,n 這個區間內素數的個數呢?小hi 你自己有什麼想法麼?小ho 有!我一開始的想法是,自然我們已經知道了如何快速判定乙...

Eular質數篩法

任意乙個正整數k,若k 2,則k可以表示成若干個質數相乘的形式。eratosthenes篩法中,在列舉k的每乙個質因子時,我們都計算了一次k,從而造成了冗餘。因此在改進演算法中,只利用k的最小質因子去計算一次k。而在其基礎上改進的eular篩法,其偽 為 isprime true primelist...