物件的繼承,super(),子類與父類的引數
質數篩
閒談
原因
蕪湖,蒟蒻的第十篇部落格。(\(NOIP\)加油!!!)
背景
之前一直很想學習這裡但是沒有抽出時間,今天身體不適待在家中,就趁機學習了一下。
質數篩
背景
我們在資訊競賽的題目當中,很多時候會看到和質數相關的問題,我們如果用傳統的遍歷法的話,時間複雜度為\(O(\sqrt{n})\),並且一次只能求解一個。
bool isprime(int n){
for(int i = 2;i <= sqrt(n); i++)
if(n % i == 0) return false;
return true;
}
但我們往往會遇到求解一個區間內的質數總數的時候,這時候往往會造成太大的時間複雜度,質數篩就是一種可以將時間複雜度降低為線性的優秀方法。
理解
首先我們來了解一下什麼叫做質數篩,首先我們開一個\(bool\)型別的陣列,將它的大小定義為我們所需要求範圍中的最大值,並將它們都賦值為真\(is_prime[maxn] = true;\),這個陣列中的元素如果為\(true\)那麼說明這個數為質數,如果為\(false\)那麼說明便不為質數,如何將合數找出來便非常的關鍵了。
埃拉託斯特尼篩法
這個過程是這樣實現的,首先從\(1\)開始,\(1\)既不是質數也不是合數,所以標記為\(false\);接著是\(2\),\(2\)是質數,便標記為\(true\),注意將\(2\)到\(maxn\)中所有能被\(2\)整除的數都標記為\(false\)
int prime[MAXN]; //素數陣列
bool isprime[MAXN + 10]; //is_prime[i]表示i是素數
//返回n以內素數的個數
int solve(int n){
int p = 0; //素數個數計數器
for (int i = 0; i <= n; i++)
is_prime[i] = true;
is_prime[0] = is_pri[1] = false; //首先標記0和1不是素數
for (int i = 2; i <= n; i++){
if (is_prime[i]){ //如果i是素數
prime[++p] = i; //將素數放進素數表
for (int j = 2 * i; j <= n; j += i) //所有i的倍數都不是素數
is_prime[j] = false;
}
}
return p;
}
但是我們會發現,例如\(6\)這個數字,會被\(2\)和\(3\)重複篩去一遍,會造成不必要的計算,時間複雜度是\(O(nlognlogn)\)所以我們將這個方法進行了改進。
尤拉篩
尤拉篩的核心是:讓每一個合數被最小質因數篩去。我們來看一個具體的例子。
\[\large{2\quad3\quad4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]\[primes:() \]這次我們還運用了一個\(**質數表**\)prime[maxn]$來維護,首先把\(2\)把它加入到質數表當中,並在原陣列中刪除;
\[\large{\color{blue}2\quad3\quad4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]\[primes:(2) \]並且用\(2\)來乘質數表中的每一個數,這裡只有\(2\),所以得出的是\(4\),所以我們將\(4\)在原陣列中劃去;
\[\large{\color{blue}2\quad3\quad\color{red}4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]\[primes:(2) \]接下來是\(3\),我們也同樣將 它加入到質數表中,並在原陣列中刪除;
\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad5\quad6\quad7\quad8\quad9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]\[primes:(2, 3) \]同時與質數表中的每一個數字相乘,得到\(6\)和\(9\),將它們劃掉;
\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad5\quad\color{red}6\quad7\quad8\quad\color{red}9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]\[primes:(2, 3) \]接下來是\(4\)(注意\(4\)也是需要遍歷的,只是不加入到質數表當中而已),我們劃掉\(8\),但是不劃掉\(12\),因為我們說尤拉篩的核心是讓每一個合數被最小的質因數篩去,應該用\(2\)去篩去\(12\)。
\[\large{\color{blue}2\quad\color{blue}3\quad\color{red}4\quad5\quad\color{red}6\quad7\quad\color{red}8\quad\color{red}9\quad10\quad11\quad12\quad13\quad14\quad15\quad16} \]\[primes:(2, 3) \]我們分析,對於一個數\(x\),我們遍歷到質數表中的\(p\)時,如果發現\(p|x\),那麼我們就應該停止遍歷整個質數表。我們來證明一下:設\(x = pr(r \geq p)\)(\(p\)為\(x\)的最小質因數),那麼對於任意的\(p' > p\),有\(p'x = pp'r = p(p'r)\),即說明\(p'x\)的最小質因數不是\(p'\),不應該在這裡劃掉。
按照這個思路我們可以得到。
程式碼如下
int cnt = 0;
bool isprime[MAXN];
int primes[MAXN];
void solve_plus(int n){
for (int i = 2; i <= n; i++){
if (!isprime[i])
primes[++cnt] = i;
for (int p = 1; p <= cnt; p++){
if (p * i > n)
break;
isprime[p * i] = 1;
if (i % p == 0)
break;
}
}
}
那麼這就是我們在資訊競賽中最常用到的兩種質數篩了,多多敲程式碼更有助於理解。
完結撒花ヾ(✿゚▽゚)ノ