學習筆記——質數

從本節開始,咱們將進入數學的海洋(但願不會溺水),那就從質數開始咱們的數學知識之旅吧!算法

一、定義

若一個正整數沒法被除了\(1\)和它自身之外的任何天然數整除,則稱該數爲質數(或素數),不然稱該數爲合數。數組

--《算法競賽進階指南》ide

在整個天然數集合中,質數的分佈比較稀鬆,大約\(lnN\)個數中有一個質數,即對於正整數\(N\),不超過其的質數大約有\(\frac{N}{lnN}\)個。優化

2.質數的斷定

試除法:若一個正整數\(N\)爲合數,則存在一個能整除\(N\)的正整數數\(T\),知足\(2 \le T \le \sqrt{N}\)spa

理解:若存在一個正整數\(M\)使得\(M|N\)\(N\)\(M\)整除)且\(M \in (\sqrt{N},N)\),則正整數\(\frac{N}{M}\)也能夠整除\(N\)\(\frac{N}{M} \in [2,\sqrt{N}]\)code

因此判斷一個數是否爲質數,咱們只須要掃描$2 - \sqrt{N} \(之間的全部整數,依次判斷其是否整除\)N\(,都不整除則\)N\(爲質數,不然爲合數。其時間複雜度爲\)O(\sqrt{N})$。blog

代碼以下:ci

il bool id_prime(int n) {
//質數返回1,合數返回0
	if(n<2) return 0;
	//特判0和1,它們是合數
    for(re int i=2;i*i<=n;++i) //依次掃描
    	if(n%i==0) return 0;//能被整除,就是合數
    return 1;//是質數
}

固然,咱們也能夠對其進行一些常數優化:get

il bool is_prime(int n) {
	if(n<2) return 0;
   	if(n==2||n==3) return 1;
   	if(n%6!=1&&n%6!=5) return 0;
  	for(re int i=5;i*i<=n;i+=6) 
		if(n%i==0||n%(i+2)==0) return 0;
	return 1;
}

此方法基於一個原理:博客

\(2\)\(3\)外的全部質數,除以\(6\)的餘數必定爲\(1\)\(5\)

理解:一個數除以\(6\),其他數爲\(0\)\(1\)\(2\)\(3\)\(4\)\(5\)

對於餘數爲\(0\)\(2\)\(4\)的數,其必定是\(2\)的倍數。

對於餘數爲\(0\)\(3\)的數,其必定是\(3\)的倍數。

因此咱們只要判斷\(N\)是否整除\(6n-1\)\(6n+1\)便可(其中\(n \ge 1\)\(6n+1 \le \sqrt{N}\)

這種方法常數極小,大約爲\(\frac{\sqrt{N}}{3}\)

3.質數篩

題意簡述:給定一個正整數\(N\),求\(1-N\)之間全部質數。

首先,咱們想到的最樸素的想法就是枚舉\(1-N\),依次判斷每一個數是不是質數。顯然,這樣作太慢了,咱們就能夠引進一些算法來優化它。

Eratosthenes 篩法

就是\(OIer\)們口中常說的埃氏篩,它的基本思路是:任何一個整數\(x\),其倍數\(2x\)\(3x\)......\(\left\lfloor\dfrac{N}{x}\right\rfloor \times x\)都是合數。

實際上,小於\(x^2\)\(x\)的倍數在掃描更小的數時已經被標記過了

理解:\(2x\)在標記\(2\)時標記了,\(3x\)在標記\(3\)時標記了.....前\(2\)\(x-1\) \(\times x\)都在標記比\(x\)小的質數時被標記過了。

因此咱們只須要從\(x^2\)標記到\(\left\lfloor\dfrac{N}{x}\right\rfloor \times x\)便可,其時間複雜度爲\(O(\sum_{p \le N}{\frac{N}{p}}) = O(N log log N)\),其中\(p\)表示小於\(N\)的質數。

代碼以下:

bool vis[N];//標記數組
int pri[N],m;//pri[i]記錄質數,m表示質數個數
il void primes(int n) {
	for(re int i=2;i<=n;++i) {
   		if(vis[i]) continue;//標記過,直接下一層循環
   		pri[++m]=i;//統計質數
    	for(re int j=i;j<=n/i;++j) vis[i*j]=1;//標記i的倍數
   	}
}

歐拉篩(線性篩)

咱們都知道,埃氏篩最大的瓶頸就是會重複標記質數(即便從\(x^2\)開始標記也會),那咱們可不可讓每個數只被標記一次呢?答案是確定的,歐拉篩就給咱們提供了這樣的思路:其經過從小到大累計質因子的方法,令\(vis[i]\)數組只記錄\(i\)的最小質因子,達到線性篩質數。

其過程以下:

  • 掃描\(2\)\(N\)中的每一個數,若vis[i]==0,則令vis[i]=i並把它保存進質數數組。

  • 掃描質數數組中不大於\(vis[i]\)的全部質數(設爲\(p\)),標記vis[i*p]=p。(由於\(p \le vis[i] \le i\),因此\(p\)是合數\(i \times p\)的最小質因子)

由於每個合數均可以被表示成\(i \times p\)的形式,且其只會被其最小質數\(p\)篩一次,因此其時間複雜度爲\(O(N)\)

代碼以下

int vis[N];//標記數組
int pri[N],m;//pri[i]記錄質數,m表示質數個數
il void primes(int n) {
	for(re int i=2;i<=n;++i) {
   		if(!vis[i]) {vis[i]=i;pri[++m]=i;}//沒被標記過,是質數
      	for(re int j=1;j<=m;++j) {//枚舉質數數組
      		if(pri[j]>vis[i]||pri[j]>n/i) break;
          	//pri[j]不是i*pri[j]的最小質數或i*pri[j]>n就不用繼續標記
      		vis[i*pri[j]]=pri[j];
          	//標記i*pri[j]的最小質因子pri[j]
      	}
   	}
}

3.質因數分解

算數基本定理:任何一個大於\(1\)的正整數都能惟一分解爲有限個質數的乘積。可寫做:

\[N = p_1^{c_1}p_2^{c_2}...p_m^{c_m} \]

其中\(c_i\)都是正整數,\(p_i\)都是質數,且知足\(p_1 < p_2 < ... < p_m\)

個人理解:

首先,根據質數的定義,質數只能被一和它自己整除,因此質數符合這個定理

又由於合數能被除了一和它自己的數整除(設這個數爲\(p\)),若是\(p\)是質數,那就符合這個定理, 若是\(p\)是合數,就繼續往下分。

試除法

咱們能夠掃描\(2 \sim \left\lfloor{\sqrt{N}}\right\rfloor\)中的每一個數\(d\),若\(d|N\),則從\(N\)中除去全部的因子\(d\),同時累計除去的\(d\)的個數。

由於合數確定在以前就被其質因數除去,因此最後得出的必定是質因數分解的結果,時間複雜度爲\(O(\sqrt{N})\)

代碼以下:

int pri[N],c[N],m;//p[i]記錄質因數,c[i]記錄第i個質因數的個數
il void divide(int n) {
	for(re int i=2;i*i<=n;++i) {
   		if(n%i==0) {//找到能夠整除的數
      		pri[++m]=i,c[m]=0;
      		while(n%i==0) n/=i,++c[m];//統計該質因子的個數
    	}
   	}
   	if(n>1) pri[++m]=n,c[m]=1;//若是最後n也是質數,把n加入答案數組
}

例題:

P3383 【模板】線性篩素數(板子)(題解)(個人代碼)

P5535 【XR-3】小道消息(題解)(個人代碼)

本題是一道有意思的素數判斷題(幸虧出題人很良心),根據伯特蘭-切比雪夫定理:若整數\(n > 3\),則至少存在一個質數\(p\),符合\(n\) \(<\) \(p\) \(<\) \(2 \times n\) \(-\) \(2\),咱們就能夠對\(k+1\)進行分類討論:

  • \(k+1\)是質數:則第一天它能夠傳遍全部不是它的倍數的數(質數與全部不是它倍數的數互質),次日就能夠由不是它的倍數的數\(-1\)傳給它(相鄰的兩個數互質)。(顯然,若是\(2 \sim n+1\)都沒有該質數的倍數,一天就傳完了)

  • \(k+1\)不是質數:則第一天它能夠傳遞到一個屬於\([\left\lfloor\dfrac{n}{2}\right\rfloor,n]\)中的一個素數,再由該素數傳遞到全部數。

AcWing196. 質數距離(題解)(個人代碼)

本題中\(l\)\(r\)特別大,但\(r-l \le 1e6\),因此咱們能夠預處理\(2 \sim \sqrt{r_{max}}\)中的全部質數,在對於每一組\(l\)\(r\),將\(l \sim r\)中間的全部質數的倍數標記,在剩下的未標記的數中找相鄰的最大值和最小值便可。

注意點:

  • \(l<2\)是,把\(l\)設爲\(2\)

  • 標記區間的合數時,可讓該區間都減去\(l\),只用開一個\(1e6\)\(bool\)數組便可。

AcWing197. 階乘分解(題解)(個人代碼)

思路:由於\(1 \sim n\)中的數的質因子最大不超過\(n\),因此能夠先預處理出\(1 \sim n\)中的全部質數,設質數爲\(p\),則\(N!\)中質因子\(p\)的個數爲:

\[\left\lfloor\dfrac{N}{p}\right\rfloor + \left\lfloor\dfrac{N}{p^2}\right\rfloor + \left\lfloor\dfrac{N}{p^3}\right\rfloor + ... + \left\lfloor\dfrac{N}{p^{\left\lfloor{log_pN}\right\rfloor}}\right\rfloor = \sum\limits_{p^k \le N}\left\lfloor\dfrac{N}{p^k}\right\rfloor \]

對於每一個\(p\),咱們只須要\(O(logN)\)的時間計算上述和式,因此整個算法的時間複雜度爲\(O(NlogN)\)

參考資料

相關文章
相關標籤/搜索