所谓素性测试是检测一个数是否为素数的测试。而对素数的研究是有很长一段历史,把素数的东西写成一本书的话也许得上千页,而现代密码学又加深了科研工作者对素数的研究,今天就以输出100以内的素数的为例,讲讲素性测试的几种方法。
这可能是每个学过计算机的朋友都敲过的代码,原理就是从判断2到sqrt(n)或者n/2能不能整除n,若能整除就不是素数。
代码清单:
public class SuXingCeShi { public static void main(String[] args) { int n = 100; SuXingCeShi robot = new SuXingCeShi(); robot.shichu(n); } private void shichu(int n) { for(int i = 2; i <= n; i++){ int j; for(j = 2; j < Math.sqrt(i); j++){ if(i % j == 0) break; } if(j > Math.sqrt(i)) System.out.print(i + " "); } System.out.println(); } }
步骤:
1.列出2,3,4..........100
2.选取第一个没被处理过的数(第一次为2)
3.去掉以2为因数的所有数
4.如果下一个素数小于sqrt(100)跳转到第二步
5.结束
以下是来至wikipedia介绍该算法的图片,挺直观的,可以看看
代码清单:
public class SuXingCeShi { public static void main(String[] args) { int n = 100; SuXingCeShi robot = new SuXingCeShi(); robot.eratosthenesShaixuan(n); } public void eratosthenesShaixuan(int n) { // TODO Auto-generated method stub int[] a = new int[n+1];//申请n+1个空间 a[0] = a[1] = -1;//剔除0,1 for(int i = 2; i < n+1; i++) a[i] = i; int temp = 2; double edge = Math.sqrt(n);//边界值 while(temp <= edge){ if(a[temp] == -1){//如果a[temp]是合数 temp++; continue; } //如果a[temp]是质素,去掉以它为因数的合数 for(int i = 2; i <= (a.length-1)/temp ; i++){ a[temp*i] = -1; } temp++; } for(int i = 0; i < a.length; i++){ if(a[i] != -1) System.out.print(a[i] + " "); } System.out.println(); } }
eratosthenes算法在拆选的时候进行了重复拆选,比如数30,2、3、5都对其进行了拆选,有没有一种更好的方法,让每个合数只被拆选一次呢?
这个算法稍稍有些难理解,先看代码
public class SuXingCeShi { public static void main(String[] args) { int n = 100; SuXingCeShi robot = new SuXingCeShi(); robot.caixuan(n); } void caixuan(int n){ boolean flag[] = new boolean[n+1]; int prime[] = new int[n+1]; int prime_num = 0; for(int i=0; i<=n; i++){ flag[i] = true;//把所有数都看成素数 } for(int i = 2; i <= n; i++){ if(flag[i] == true){ prime[prime_num++] = i;//记录素数 } for(int j=0; j <prime_num && prime[j]*i <= n; j++){ flag[prime[j]*i] = false;//把合数赋值成false if(i % prime[j] == 0) break; } } for(int i = 0; i < prime.length; i++){ if(prime[i] != 0) System.out.print(prime[i] + " "); } } }我们先申请一个boolean[] flag来进行拆选,再申请一个int[] prime记录素数,在第二个for循环中进行判断,if(flag[i] == true)把它记录下来(因为我们把i之前的数都拆选过了,看二重for循环)比如现在 i = 9; i 前面的素数有2,3,5,7都记录在prime[]里了 然后遍历prime数组,j = 0时,flag[prime[j]*i] = false意思是把flag[2*9] 赋值成false,j = 1时候我们把flag[3*9] 赋值成false,关键是下面这句话,if(i % prime[j] == 0) break;它是整个算法的核心,为什么把flag[27]筛出去之后就不再筛flag[36]和flag[45]了呢?因为当i = 18的时候 flag[2*18] 把flag[36]给筛出去了,但是没有筛选flag[3*18],flag[4*18] ,因为在flag[2*27]和flag[2*36]分别拆选过了。再举个例子比如i = 35,当flag[2*35]拆选过后 ,还得筛选flag[3*35](假设n>105),因为flag[105]没法再由其他形式拆选了,所以用这样方式才保证了每个合数被筛选且仅被筛选一次。
读过数论的朋友一定看过wilson算法,在数论中,威尔逊定理给出了判定一个自然是否为素数的充分必要条件,即:当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p )
如果您没接触过数论,帮您解释下这个式子是啥意思,(mod p)叫同余 意思就是除以p后余数相等,可以把上述式子理解为当且仅当p为素数时,[(p-1)!+1]%p == 0
显然这个算法时间复杂度和需要数据类型的表示范围之大都是无法回避的,实际操作不可行,理论价值非常高。
代码清单:
public class SuXingCeShi { public static void main(String[] args) { int n = 100; SuXingCeShi robot = new SuXingCeShi(); robot.wilson(n); } public void wilson(int n){ for(int i = 2; i <= n; i++) if(isPrimeFromWilson(i)) System.out.print(i + " "); System.out.println(); } private boolean isPrimeFromWilson(int m) { long temp = 1; int i = m-1; while(i >= 1){ temp *= i; i--; } return (temp+1) % m == 0; } }
首先介绍一个数论中非常非常重要的一个定理-----费马小定理
假如a是一个整数,p是一个素数,那么 (0 < a < p)
换句话说也就是:如果p是一个素数,且0<a<p,则a^(p-1)%p=1 但是费马小定理是判断素数的必要条件,当a=2时候,有一类伪素数数,它们满足费马小定理,但其本身却不是素数。它们有无穷个,最小的伪素数是341,统计表明前10亿个数中有50847534个素数,满足2^(p-1)%p = 1的伪素数一共有5597,出错概率非常之低,所以我们可以根据我们的需求建一表伪素数表,算出素数看是否存在这张表里(可以二分,hash,trie)
好了,具备了以上知识我们想想费马小定理的函数该怎么写?
如果直接算2^(p-1)数的表示范围很头疼,于是我们可以利用这个迭代公式来计算
代码清单:
int runFermatPower(int a, int b, int n) {//返回a^b mod n int result = 1; for (int i = 0; i < b; i++) { result *= a; result %= n; } return result; }
int runFermatPower(int a, int b, int n)// d≡a^b mod n { int result = 1; while (b > 0) { if ((b & 1) == 1) result = (result * a) % n; b >>= 1; a = (a * a) % n; } return result; }
public void femat(int n){ for(int i = 2; i <= n; i++){ if(runFermatPower(2, n-1, n) != 1){ if(i不在伪素数表中) 输出i; } } }
int runFermatPower(int a, int b, int n)// a^b mod n { int result = 1; while (b > 0) { if ((b & 1) == 1) result = (result * a) % n; if ((a * a) % n == 1 && a != 1 && a != n - 1) return -1;// 二次探测 b >>= 1; a = (a * a) % n; } return result; }
import java.util.Random; public class SuXingCeShi { public static void main(String[] args) { int n = 100; SuXingCeShi robot = new SuXingCeShi(); robot.getAllPrime(n); } int runFermatPower(int a, int b, int n)// d≡a^b mod n { int result = 1; while (b > 0) { if ((b & 1) == 1) result = (result * a) % n; if ((a * a) % n == 1 && a != 1 && a != n - 1) return -1;// 二次探测 b >>= 1; a = (a * a) % n; } return result; } public boolean runMillerRabin(int n, int time) { Random random = new Random(); for (int i = 0; i < time; i++) { if (runFermatPower(random.nextInt(n - 1) + 2, n - 1, n) != 1) return false; } return true; } public void getAllPrime(int n) { int time = 100; for (int i = 2; i <= n; i++) { if (runMillerRabin(i, time) == true) { System.out.print(i + " "); } } } }
==================================================================================================
作者:nash_ 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/nash_/article/details/8290774
===================================================================================================