假期集訓第二輪是數論,是一直以來不願意動手的一個分支,忽然發現什麼都不會了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. 素數篩
- 一般篩法(埃拉託斯特尼篩法):
素數的倍數一定不是素數
先初始化一個全是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 大佬題解裏看吧
我,,,沒搞懂,而且不穩定,不敢用。。。