素数在数论中经常被用到。也是数论的基础之一。
人们一直在讨论的问题是,怎样快速找到素数?或者判断一个数是素数?
1.根号n枚举
原始暴力方法。
2.埃氏筛
每个合数会被筛质因子次数次。复杂度O(NloglogN)
3.线性筛素数
每个合数只会被它的最小质因子筛一次。
线性筛还可以筛各种函数
具体见:SIEVE 线性筛
4.Miller_Rabin
利用:二次探测,费马小定理。
二测探测:
若P是质数,那么若x^2=1 mod P,则,x=1或者P-1
证明:即x^2-1 = 0 mod P,P|(x-1)*(x+1)
由于P是质数,所以一定是在(x-1)或者(x+1)里面存在P的质因子。
所以,有P|(x-1)或者P|(x+1),所以,x=1,或者,x=-1=P-1
如果有x^2=1 mod P,但是不满足x=1或者x=P-1,那么这个P一定不是素数。
费马小定理:
对于质数P,任意整数a(a!=P) 都有,a^(P-1)=1 mod P
这个也可以作为质数的判定条件。存在一个a不满足P一定也不是质数,。
把这两个结合起来,就可以进行Miller_Robin算法了。
具体地:
1.传入一个数n,lp=n-1
再传入一个随机数a,但是一般是质数,如2,3,5,7,61,等等
2.把lp中的所有质因子2都提出来,提出来之后的数设为d,s记录2的次数。
3.令$t=a^d mod n$,如果t==1或者t==n-1那么返回true
因为,再把s都乘回去的时候,一定会得到$a^{n-1}=1 mod n$
其实s为0,也可以直接返回false,因为n一定就是一个偶数了。
4.然后不断把s往回乘,其实是平方,因为d在指数的位置。
记录平方之前的数las,之后,如果t==1而las!=1并且las!=n-1那么就返回false
(根据二次探测)
5.s乘完后,如果n是质数,那么t=1,(根据费马小定理)。否则返回false
6.是true的话,返回1多试几次。
7.输出结果。
据说试1次,误判概率为1/4,那么试4次误判的概率就是1/(4^4)很小了。
模板:
luoguP3383(用Miller_Rabin过线性筛)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100000+5;
int a[3]={2,7,61};
ll qm(ll x,ll y,ll mod){
ll ret=1;
while(y){
if(y&1) (ret*=x)%=mod;
(x*=x)%=mod;
y>>=1;
}
return ret;
}
bool che(ll a,ll x){
int s=0;
ll t;
ll lp=x-1;
while(!(lp&1)){
s++;
lp>>=1;
}
t=qm(a,lp,x);
if(t==1||t==x-1) return true;
if(s==0) return false;
ll las=t;
for(int i=0;i<s;i++){
las=t;
(t*=t)%=x;
if(t==1&&(las!=1&&las!=x-1)) return 0;
}
if(t!=1) return 0;
return 1;
}
int n,m;
bool M_R(ll n){
if(n==2||n==7||n==61) return true;
if(n<2||n%2==0||n%7==0||n%61==0) return false;
for(int i=0;i<3;i++){
ll b=a[i];
if(b!=n){
if(!che(b,n)) return false;
}
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
ll x;
for(int i=1;i<=m;i++){
scanf("%lld",&x);
if(x==2||x==3||x==5||x==7||x==11||x==13){
puts("Yes");continue;
}
if(x%2==0||x%3==0||x%5==0||x%7==0||x%11==0||x%13==0){
puts("No");continue;
}
if(M_R(x)) puts("Yes");
else puts("No");
}
return 0;
}
/*
Author: *Miracle*
Date: 2018/9/24 20:50:22
*/