【数论基础】素数判定和Miller Rabin算法
判断正整数p是否是素数
方法一 朴素的判定
方法二 线筛
对于大整数(>1e9),可以优化
方法三 利用费马小定理
假如p是质数,且gcd(a,p)=1,那么 a^(p-1) ≡1(mod p)。
即:假如a是整数,p是质数,且a,p互质,那么a的(p-1)次方除以p的余数恒等于1。
我们这样写:a*a^(p-2) ≡1(mod p)
根据定义,a^(p-2)就是a (模p)的逆元
那么反过来,随机整数a能使p满足a^(p-1) ≡1(mod p)的话,那么p就是质数?
当然不是,虽然很大可能是,但是会有特殊的存在。
如2^340≡1(mod 341),但341=31*11
具体来说,特殊的数是费马伪素数和卡迈克尔数,这里不介绍了
方法四 二次探测定理
对于任意素数p, 满足方程
有唯一解 x=1||p-1
证明
方法五 Miller Rabin算法
逆用费马小定理加上二次探测,就得到了 Miller Rabin 算法。
Miller Rabin 算法有一定的出错概率,出错的情况一定是将合数判定为素数,且出错概率极低
对于一个奇素数(偶素数只有2嘛),显然p-1是偶数
则有推导过程如下:
所以算法实现过程有两步:
1.枚举k,对于每一个k,检验是否满足二次探测定理(即上面的最后一步)
2.再检验即是否满足费马小定理
对于任意一个p,如果通过了算法检验,则它不是素数的概率是25%
那么k轮之后,p不是素数的概率就是0.25^k,就很小了
一般8-10轮就差不多了
那么对于a的选择,可以选前面几个质数:2,3,5,7,11等,也可以用随机数
时间复杂度一般是,k为测试的轮数
//里面有龟速乘和快速幂,防越界 inline int slow_mult(int a,int b,int mod){ int ans=0; while(b){ if(b&1) ans=(ans+a)%mod; a=(a+a)%mod; b>>=1; } return ans; } inline int quick_pow(int a,int b,int mod){ int ans=1; while(b){ if(b&1) ans=slow_mult(ans,a,mod); a=slow_mult(a,a,mod); b>>=1; } return ans; } inline bool Miller_Rabin(int p){ if(p==2) return true; if(p==1||p%2==0) return false; int u=p-1,t=0; while(u%2==0) u/=2,t++; for(int i=0;i<=9;i++){//我测了十次 int a=rand()%(p-1)+1; int x=quick_pow(a,u,p); if(x==1) continue; for(int j=1;j<=t;j++){ int y=slow_mult(x,x,p); if(y==1 && x!=1 && x!=p-1) return false;//二次探测定理 x=y;//递推求2的幂 } if(x!=1) return false;//费马小定理 } return true; }