miller_rabin素数检测
今天学习了一下miller_rabin素数检测 稍微总结一下
首先用到两个定理
1、费马小定理
假如p是质数,且gcd(a,p)=1,那么a(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
2、二次探测定理
如果p是一个素数, 0<x<p,则方程x^2≡1(mod p)的解为 x=1或x=p-1
很可惜 费马小定理的逆命题是不成立的 即如果 a(p-1)≡1(mod p)推不出p是质数 但是也很庆幸 在大部分情况下它是成立的只有极少数的反例 所以我们依然可以用费马小定理的逆命题来概率性的判断素数 虽然会犯错 但是犯错概率也是相对较小的 为了进一步提高它判断质数的准确性 我们结合了定理二
证明定理二
其实 x^2≡1(mod p)
相当于 x^2-1≡0(mod p)
也就是 (x+1)(x-1)≡0(mod p)
也就是说 p整除(x+1)或者(x-1) 所以 x=1 或者 x=p-1
然后就是素数检测的过程
对于待检测数p 在[2,p]中随机选取s次a 判断 a(p-1) ≡ 1(mod p)是否成立 一旦发现不成立则可判定p不是素数 为了提高正确的概率在求 a(p-1)%p 的过程中用到定理二 令p-1 = 2^t * k (其中k是奇数) 然后 a(p-1) 就相当于 ak*2^t 也就是将 ak 平方t次 用x[i] 来表示平方i次的结果 如果发现 x[i] = 1 而x[i-1]既不等于1也不等于p-1 那么就可以判定p不是素数了 求 a(p-1)%p 的过程用的是快速幂 在用快速幂的过程中还用到了一个大数相乘取模的优化 即化乘法为加法 即模拟两个数相乘的二进制运算过程 每次相加取模 如果p成功通过检测则判定为素数否则为合数
快速幂算法不太清楚的可以看一下这里 http://blog.csdn.net/mm__1997/article/details/71794859
实现代码:
#include <stdio.h>
#include <stdlib.h>
typedef long long LL;
LL x[105];
LL multi(LL a,LL b,LL p){// 化乘法为加法
LL temp=0;
while (b){
if (b&(LL)1) temp=(temp+a)%p;
a=(a<<1)%p;b>>=1;
}
return temp;
}
LL qpow(LL a,LL b,LL p){// 快速幂
LL temp=1;
while (b){
if(b&(LL)1)
temp=multi(temp,a,p);
b>>=1; a=multi(a,a,p);
}
return temp;
}
int miller_rabin(LL n){
if (n==0||n==1) return 0;
if (n==2) return 1;
int s=10; LL k=n-1;
int t=0;
while (!(k&(LL)1)){t++;k>>=1;};// k如果不是奇数将k转化为奇数
while (s--){
LL a=rand()%(n-2)+2;// 随机取值
x[0]=qpow(a,k,n);
for (int i=1;i<=t;i++){
x[i]=multi(x[i-1],x[i-1],n);// 记录平方i次的结果
if (x[i]==1&&x[i-1]!=1&&x[i-1]!=n-1) return 0;
}
if (x[t]!=1) return 0;
}
return 1;
}
int main (){
LL n;
while (scanf ("%lld",&n)!=EOF){
if (miller_rabin(n)) printf ("%lld is a prime\n",n);
else printf ("%lld is not a prime\n",n);
}
return 0;
}
Java实现代码: http://blog.csdn.net/mm__1997/article/details/78307999