Miller-Rabin素性测试(POJ3641)
一.概念引入
在以往判断一个数n是不是素数时,我们都是采用i从2到sqrt(n)能否整除n.如果能整除,则n是合数;否则是素数.但是该算法的时间复杂度为O(sqrt(n)),当n较大时,时间性能很差,特别是在网络安全和密码学上一般都是需要很大的素数.而从目前来看,确定性算法判断素数的性能都不好,所以可以用MC(蒙特卡洛)概率算法来解决,其中Miller Rabin算法就是其中的很经典的解决方法.下面首先介绍下相关的数学理论。
理论基础:Fermat小定理:若n是素数,则对所有1≤a≤n-1的整数a,有a^(n-1)mod n=1;该定理的逆否命题也成立,即a^(n-1)mod n!=1,则n为合数。但是如果n是素数,就不一定成立了,比如当a=4,n=15时,4^14mod15=1,但是4不是素数而是合数。
不过,从大量数据统计来看,如果满足a^(n-1)mod n=1,则n较大概率为素数。那么,我们把那些使得n原本为合数而被看成素数的a叫做伪证据,n为伪素数。同样从大量数据看出有些伪素数n有很多伪证据a,比如当n=561,a有318个可使得结果判为素数。所以,读者可以去查查强伪素数的概念。
二.POJ3641
判断p是否是基于a的伪素数。
import java.util.Random; import java.util.Scanner; public class Main { public static void main(String[] args) { int p,a; Scanner sc = new Scanner(System.in); while(true) { p = sc.nextInt(); a = sc.nextInt(); if(p==0&&a==0) { break; } boolean flag1 = fermat(a,p-1,p); boolean flag2 = millerRabin(p); //System.out.println(flag1+" "+flag2); if(flag2) {//如果是素数,那肯定不是伪素数 System.out.println("no"); }else { if(flag1) { System.out.println("yes"); }else { System.out.println("no"); } } } } private static boolean millerRabin(int p) {//进行20次 boolean flag = p==1 || (p!=2&&(p%2)==0) || (p!=3&&(p%3)==0) || (p!=5&&(p%5)==0) || (p!=7&&(p%7)==0); if(flag) { return false;//表示合数 } int times = 20; Random r = new Random(); for(int i=0; i<times; i++) { if(fermat(r.nextInt(p-1)+1,p-1,p)==false) return false; } return true; } private static boolean fermat(int a, int b, int n) { //a^(p-1)modp=1 int ans = 1; for(int i=0; i<b; i++) { ans = ans*a%n; } return ans==1; /* * 或者 * while(b>0) { if(b&1) { ans=ans*a%n; } b>>=1; a=a*a%n; * } */ } }
作者:火星十一郎
本文版权归作者火星十一郎所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.