1. 程式人生 > 其它 >一個數等於兩個不同素數的乘機_素數——常用的初等數論知識及在演算法領域的應用...

一個數等於兩個不同素數的乘機_素數——常用的初等數論知識及在演算法領域的應用...

技術標籤:一個數等於兩個不同素數的乘機判斷一個數是否為兩個素數乘積判斷多個數據是否是兩個素數相乘輸入一個正整數求所有素數因子

注:本文是文章《Sochiji:【持續更新】常用的初等數論知識及在演算法領域的應用》的一個章節。

2019年11月14日更新:新增了引理1和定理2的證明。

2020年2月4日更新:修正了定理1的證明中的一處錯誤;重寫“判斷單個大於1的整數的素性”章節;新增“製作素數表”章節。

2020年2月5日更新:修正了多處錯誤;在“製作素數表”章節新增了尤拉篩法。

本文目錄

1.有關素數的知識

2.判斷單個大於1的整數的素性

3.製作素數表

有關素數的知識

定義

為大於1 的整數,如果正整數
整除 且有 ,則說 真因子。當 沒有真因子時稱為 素數質數,否則說 合數 的真因子中的素數稱作 素因子
一般用 表示不超過 的素數的個數, 表示由小到大排列的第 個素數。

引理1:最小素因子

對於整數

,它的大於1的因子中最小的一個必為素數。
這個素數稱為 最小素因子。對於大於1的整數 ,我們用 表示 的最小素因子。

證明:

的大於1的因子中最小的一個,假設 為不是素數,則存在真因子 。由於 ,則有
。由於 ,這表明小於 也是 的真因子,即 不是 的大於1的因子中最小的一個,與 假設矛盾,故 為素數。

定理1

素數有無窮多個。

證明:

引理2:設正整數 ,則 互素。
記已知的從小到大排列的前 個素數的乘積為 ,由於 互素,那麼 的最小素因子不在 之列,這樣我們就構造出了一個新的素數,記為 。同樣地 的最小素因子不在 以及 之列。上面的方法總是可以構造出新的素數,故素數有無窮多個。

定理2

從小到大排列的第

個素數 不超過

定理3

,且 ,則 為素數當且僅當不超過 的素數都不整除

定理4:算術基本定理

任何大於1的整數都可以表示成有限個(可重複)素數的乘積,而且不計乘積中因子順序時這種分解還是唯一的。

對一個整數進行上述分解的操作稱為 素因數分解。 例:

定理5:素數定理

,則

判斷單個大於1的整數的素性

基本思想

根據定義及定理3,若一個大於1的整數

不是素數,則其有 真因子,且其真因子不會超過

嘗試找出

的任意一個真因子,若能找得到,則 為合數,否則為素數。

實現方法

先用一定的方法列舉可能的真因子,並驗證每個列舉的數是否為真因子。若是,則停止列舉,確定

為合數;若列舉完也沒發現真因子,可確定 為素數。

由真因子的定義,我們可以遞增列舉每個大於等於2且小於

的整數來做 整除性試驗。要驗證被列舉的數是否為 的因子,只需用 取模運算來判斷整除性即可。在目前絕大多數的計算機上,進行除法運算的指令相對耗時。因此我們要儘可能地減少做取模運算的次數。

優化思路1:根據定理3,若

為合數,則 的最小真因子不可能超過 ,否則 為素數。

方案1:遞增列舉每個大於等於2且不超過

的整數來做整除性試驗。

優化思路2:根據整除關係的傳遞性(的逆否命題),若2不是

的因子,則任何偶數都不是 的因子。

方案2:先用2試驗,再從3開始遞增列舉每個小於等於

奇數來做整除性試驗。

優化思路3:現提出引理3:在大於等於6的範圍內,每6個連續的自然數中至多有2個是素數。

證明:假設正整數 ,任何 不小於6的整數都可以被表示成 , , , , , 其中的一種。其中 , , , 分別有因數 ,因此它們不可能是素數。只有形如 , 的數才有可能是素數,而每6個連續的自然數中僅有一個形如 的數,也僅有一個形如 的數,故 引理3得證。

方案3:我們只需用小於等於

的數中的 (如果有的話)以及形如 的數來做整除性試驗。

優化思路4:根據引理1,遞增列舉過程中發現的第一個因子必為素數,因此可以遞增列舉小於等於

素數來做整除性試驗

方案4:在已求得不超過

的素數表的情況下,遞增列舉其中的 素數來做整除性試驗。

根據定理5:素數定理

經過 方案4優化後,要列舉的次數可以被壓縮到未經優化的大約 倍。

製作素數表

1.素數表的儲存方法

從小到大儲存每個素數(

表):
int 

優點:陣列的索引與

有著非常緊密的聯絡,根據給定的 查詢 的時間複雜度為

缺點:無法在

的時間內查詢一個任意給定的數的素性,也無法在 的時間內根據給定的 反查找出 ,上述兩種操作的時間複雜度與 在有序陣列中查詢指定元素的時間複雜度相當。

記錄每個數的素性(素性對映表):

bool 

優點:查詢一個任意給定的數的素性的時間複雜度為

,儲存空間佔用少。

缺點:無法在

的時間內根據給定的 查詢 ,也無法在 的時間內根據給定的 反查找出 ,執行上述兩種操作的時間複雜度為

記錄累計素數的個數(

表):
int 

優點:陣列的索引與

有著非常緊密的聯絡,根據給定的 反查詢 的時間複雜度為 ;由於“ 是素數 ”等價於 ,故查詢一個任意給定的數的素性的時間複雜度為

缺點:儲存空間佔用率高。無法在

的時間內根據給定的 查詢 ,執行上述操作的時間複雜度與 在有序陣列中查詢指定元素的時間複雜度相當。

若已求得以上的任意一種方式儲存的素數表,可以在

的時間內生成另外兩種方式儲存的素數表。

2.埃拉托色尼篩法

基本思想:對於一個給定的範圍

,找出不超過 的素數,篩掉這些素數的不超過 的倍數(素數自身除外)。過程完畢後,剩下沒被篩掉的數就是素數。

定理3可知上述方法是正確可行的。

例:製作不超過25的素數表。
找出不超過5的素數,有2,3,5三個數;我們現在要通過這幾個素數來尋找不超過25的合數並篩去,剩下的就都是素數了。
篩去比2大的2的倍數:4,6,8,10,12,14,16,18,20,22,24;
篩去比3大的3的倍數:6,9,12,15,18,21,24;
篩去比5大的5的倍數:10,15,20,25;
沒被篩去的數就是素數:2,3,5,7,11,13,17,19,23.

程式設計實現(C++)

//製作不超過n的素數表, 陣列primeMap[]儲存每一個數的素性。

程式設計實現埃拉托色尼篩時,我們無需事先準備不超過

的素數表(程式碼中所稱 小素數的列表)。因為在執行 if (primeMap[i])時,若 i是合數,則 i一定已經在之前被它的素因子篩掉了。

迴圈變數j是用來列舉素數i的倍數的,它的初值從i * i起,因為i的更小的倍數(例如i * (i - 1))一定有更小的素因子, 在之前就會被篩掉。


3.尤拉篩法

在埃拉托色尼篩法的執行過程中,如果一個合數有多於2個的真因子,這個合數將會被多次枚舉出。重複列舉一個合數不是演算法必需的,只會消耗更多CPU時間。而尤拉篩法能讓每個合數被枚舉出僅一次。

基本思想:對於一個給定的範圍

,篩去不超過 的合數。過程完畢後,剩下沒被篩掉的數就是素數。每個合數僅能由“它的 最小最大的真因子相乘”這一種方法枚舉出。

步驟:

【1】我們先從2開始遞增列舉整數,作為即將要被篩去的合數的最大真因子,記為

【2】對於上面選出來的每一個

,我們要選取更小的數 ,作為即將要被篩去的合數的 最小真因子
根據 引理1,最小真因子一定是素數,因此我們可以從 中遞增列舉素數來作為每次的

【3】我們選出

之後計算 就能獲得一個合數,記為 ,把它篩掉。
這組 是否一定分別是 的最大、最小真因子呢?
不一定。當存在大於 且小於 的整數 時, 的最大、最小真因子就分別是 ,而不是

【4】因此我們列舉素數作為

時,出現 後就不用列舉更大的素數,而是該返回上一層迴圈換一個 了。

【5】若

是素數, 之前不可能出現 的情況,迴圈列舉 直到 就行。
例:製作不超過25的素數表。
剩下的素數有:

程式設計實現(C++)

//尤拉篩法制作不超過n的素數表, 

程式中執行頻數最高的語句是primeMap[i * primeList[j]] = false; 從尤拉篩法的原理以及定理5:素數定理可知這條語句的執行次數關於

的函式為

掌握了尤拉篩法,推薦嘗試洛谷題庫P3383 【模板】線性篩素數。

本文完。