素數/質數判定 從判定素數/質數到埃氏篩和歐拉篩

假期集訓第二輪是數論,是一直以來不願意動手的一個分支,忽然發現什麼都不會了QAQ。今天先寫素數篩。
參考洛谷博客 zybnxy大佬 %%%一下

1. 判斷一個數是素數 時間複雜度爲O(N)

bool Is_prime(int n);
{
    if(n==1)return false;
    if(n==2)return true;
    for(int i=2;i<n;i++)
        if(n%i==0)return false;
    return true;    
}

很簡單的板子,但是應用在數據較大較多的題目,一定是不可能的。

優化

bool Is_prime(int n);
{
    if(n==1)return false;
    if(n==2)return true;
    for(int i=2;i<=sqrt(n);i++)
        if(n%i==0)return false;
    return true;    
}

在這裏插入圖片描述
然後是6N±1的優化,時間複雜對可以達到
在這裏插入圖片描述
具體講解是huang_miao_xin大佬

bool Is_prime(int n)
{
    if(n==1) return false;
    if(n==2||n==3) return true;
    if(n%6!=1&&n%6!=5) return false;//不在6的倍數兩側的一定不是質數
    for(int i=5;i*i<=n;i+=6)
        if(n%i==0||n%(i+2)==0) return false;
    return true;
}

測試到40W,時間大概是這樣的:
在這裏插入圖片描述

2. 素數篩

  1. 一般篩法(埃拉託斯特尼篩法):
    素數的倍數一定不是素數
    先初始化一個全是true的數組,代表全是素數,從第一個素數2開始,把2的倍數都標記爲非素數(置爲false),一直到大於N;然後進行下一趟,找到2後面的下一個素數3,進行同樣的處理,直到最後,數組中依然爲true的數即爲素數。
    下圖1應該也是黃色,因爲1不算是素數,初始化爲0在這裏插入圖片描述
    在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1000010;
bool prime[MAXN];
void make_prime)()
{
    memset(prime,true,sizeof(prime));//這裏注意,memset只能用來置0和-1,
    						//emmm雖然一般它能給你換,但是,,,保險點用手動
    prime[0]=prime[1]=false;
    int t=sqrt(MAXN);
    for(int i=2;i<=t;i++)
    {
        if(prime[i])
        {
            for(int j=2*i;j<MAXN,j+=i)
			    //優化:如果j 從 i * i 而不是從 i + i開始,
				//因爲 i*(2~ i-1)在 2~i-1時都已經被篩去,所以從i * i開始。
            {
                prime[j]=false;
            }
        }
    }
    return;
}

通過上述代碼,我們發現此方法還可以繼續優化,因爲上述的方法中,每一個有多組因數可能會被篩多次,例如:30會被2,3,5各篩一次導致計算冗餘,我們可以對此方法進行改進,得到更加高效的篩法 -----------------歐拉篩線性篩

歐拉篩法的基本思想 :在埃氏篩法的基礎上,讓每個合數只被它的最小質因子篩選一次,以達到不重複的目的。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1000010;
bool prime[MAXN];
int Prime[MAXN];
int num=0;
void make_prime()
{
    memset(prime,true,sizeof(prime));
    prime[0]=prime[1]=false;
    for(int i=2;i<=MAXN;i++)
    {
        if(prime[i])
        {
            Prime[num++]=i;
        }
        for(int j=0;j<num&&i*Prime[j]<MAXN;j++)
        {
            prime[i*Prime[j]]=false;
            if(!(i%Prime[j]))//解釋見下
                break;
        }
    }
    return;
}

當 i是prime[j]的倍數時,i = kprime[j],如果繼續運算 j+1,i * prime[j+1] = prime[j] * k prime[j+1],這裏prime[j]是最小的素因子,當i = k * prime[j+1]時會重複,所以才跳出循環。
eg:i = 8 ,j = 1,prime[j] = 2,如果不跳出循環,prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12,在i = 12時會計算。因爲歐拉篩法的原理便是通過最小素因子來消除。

程序理解見下圖,圖源自彤雲望月大佬
在這裏插入圖片描述
這個方法可以保證每個合數在N/M的最小素因子時被篩出,所以時間複雜度僅爲O(N)已經可以滿足大部分需求,在競賽中,這種方法也可以在極短的時間內求出關於N的質數表。

ps:每個正整數都能夠以唯一的方式表示成它的質因數的乘積。詳情見百度百科。
eg:在這裏插入圖片描述

但是這個算法的弊端在於,爲了判斷一個大數是否是素數必須從從頭開始掃描,而且空間代價也受不了,故不能離散的判斷。

所以,我們要引入更高效的算法——

Miller_Rabin算法

miller_rabin是一種素性測試算法,用來判斷一個大數是否是一個質數。 miller_rabin是一種隨機算法,它有一定概率出錯,設測試次數爲s,那麼出錯的概率是 在這裏插入圖片描述

emmm想看的去 zybnxy 大佬題解裏看吧
我,,,沒搞懂,而且不穩定,不敢用。。。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章