素数的判定
数论是算法中一块很重要的内容,而素数的判定则是数论的基础
试除法
试除法是最常用也是最基本的素数判断方法,时间复杂度:O(sqrt(n))
证明:
不妨设b能整除a,那么a/b也一定可以被b整除,那么我们不妨假设a<=a/b,则可以解出a<=sqrt(b)
上面就是试除法的理论基础
代码实现:
inline bool isPrime(int x){
if(x<2){
return false;
}
for(int i=2;i*i<=x;i++){
if(x%i==0){
return false;
}
}
return true;
}
kn+i法
kn+i法是对某些不必要的情况剪支来降低复杂度的算法,时间复杂度:O(n^(1/3))
优化技巧:
我们知道,如果一个整数不是素数,那么该整数或其倍数加上不可能成为素数的倍数也一定不是素数,例如,6不是素数,那么6k+2,6k+3,6k+4,6k+6一定也不是素数,因此,我们就只需要判断6k+1,6k+5的情况即可
代码实现:
bool isPrime(long long x){
if(n==2||n==3||n==5) return true;
if(n%2==0||n%3==0||n%5==0||n==1) return false;
//此时2,3,6已经不可能是素数因子
long long c=7;
int a[8]={4,2,4,2,4,6,2,6};
while(c*c<=x){
for(int i:a){
if(x%c==0){
return false;
}
c+=i;
}
}
return true;
}
Miller-Rabin判定法
Miller-Rabin判别法,是目前比较稳定的大数素数判别法,时间复杂度:O(klogn)
理论基础:
该判别法的理论基础是费马小定理,也就是如果p是一个素数,它需要满足对于所有和p互素的正整数a,都有下式:
上面的方程是一个同余方程,将他翻译一下,也就是a的p-1次幂减1除以p是一个整数
算法步骤:
1.简单情况特判,n==2 返回true,其余偶数情况舍弃
2.令n=m*(2^k)+1,其中m为奇数
3.枚举小于n的素数p(至多10次),对其进行费马测试
4.费马测试过程如下:计算pre=p^m%n,如果pre==1,测试失效,进行下一个数的测试,否则进行k次计算next=pre^2%n,
如果满足next==1&&pre!=1&&pre!=n-1,则n必定是合数,直接返回即可,k次计算后,如果pre的值不是1,则n必定是合数
5.直到10次测试结束之后,如果都没有检测出n是合数,则n是素数
关于费马测试:
看到这里,很多读者会疑惑这个费马测试是干啥的,下面介绍一下:
所谓费马测试,其实是一种概率型测试,也就是说它判断的是这个数是素数的概率,那么它究竟是拿什么判断的呢?
当然就是我们之前提及的费马小定理,费马小定理说,p是一个素数就满足上述公式
为了解释清楚算法的原理,我们介绍一下二次探测引理:
二次探测引理指出:如果p是一个素数,并且0<x<p,那么方程:
的解为x=1,或者x=p-1
该引理的证明如下:
我们在费马测试的过程当中,枚举了小于n的素数p,我们可以这样想:
由于费马小定理只是一种必要条件而非充要条件,因此存在极少数得特例,这些特例也被叫做“绝对伪素数”
代码实现:
typedef long long ll;
ll quick_multiply(ll a,ll b,ll m){ //快速乘,取模为m
ll ans=0,temp=a;
while(b){
if(b&1){
ans=(ans+temp)%m;
}
temp=(temp+temp)%m;
b>>=1;
}
return ans;
}
ll quick_pow(ll a,ll b,ll m){
ll ans=1,temp=a;
while(b){
if(b&1){
ans=quick_multiply(ans,temp,m);
}
temp=quick_multiply(temp,temp,m);
b>>=1;
}
return ans;
}
ll Miller_Rabin(ll n){
if(n==2) return true;
if(n<2||!(n&1)) return false;
int k=0,t=n-1;
while(!(k&1)){
k++;
t>>=1;
}
for(int i=0;i<=10;i++){
ll a=rand()%(n-1)+1;
ll b=quick_pow(a,t,n);
ll y;
for(int i=0;i<k;i++){
y=quick_multiply(b,b,n);
if(y==1&&b!=1&&b!=n-1){
return false;
}
b=y;
}
if(y!=-1){
return false;
}
}
return true;
}