素数在数论中经常被用到。也是数论的基础之一。
人们一直在讨论的问题是,怎样快速找到素数?或者判断一个数是素数?
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过线性筛)
#includeusing 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 =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 */){ las