今天放假返乡,忙了一天,晚上9点多才摸到电脑,,不说废话了看总结和代码吧:
总结:
由于今天依旧是对一些简单算法的调试与改造,不会总结太多。今天主要有两个算法,一个是筛法求[0, rightMargin]内的所有质数,一个是费马小定理快速判断质数,对于这些算法,我的解释肯定没有专业搞算法的大佬们清晰,如果有不会的朋友们请自行csdn~我在这里只展示这些算法的Java写法:
算法代码部分:
一,求区间内质数:
1.埃氏筛:
非线性,但也非常快了,原理是用基底素数去筛它们的倍数
public static void EhrlichSieve(int rightMargin){//埃氏筛 if(rightMargin < 2) { System.out.println("这个区间内没有质数!"); return; } System.out.println("0 到 " + rightMargin + " 的质数有:"); boolean[] stateArr = new boolean[rightMargin + 1]; for(int i = 2; i <= rightMargin; i++){ if(!stateArr[i]){ System.out.print(i + " "); for(int j = i; j <= rightMargin; j += i){ stateArr[j] = true; } } } }
2.欧拉筛:
线性筛,非常快(但在某些题里依然不能用)。原理是用未被筛掉的合数乘到是它因子的素数,将得到的所有乘积全部筛掉,这些乘积是用来弥补其他素数筛漏的部分,然后再乘以第一个是它因子的素数,得到下一阶为其倍数的合数,以此类推;同时素数与素数相乘也达到了互相补全的效果,最后会没有重复的把区间内所有数都筛一遍,因此也叫线性筛。
public static void EulerSieve(int rightMargin){//欧拉筛(线性筛) if(rightMargin < 2) { System.out.println("这个区间内没有质数!"); return; } System.out.println("0 到 " + rightMargin + " 的质数有:"); boolean[] stateArr = new boolean[rightMargin + 1]; ArrayList<Integer>primeArr = new ArrayList<>(); for(int i = 2; i <= rightMargin; i++){ if(!stateArr[i]) {//当前数有没有被过滤掉(是否为质数) primeArr.add(i);//如果是质数,放入数组备用 } for(int j = 0; j <= primeArr.toArray().length; j++){//从2开始,将没有过滤掉的数与已有质数相乘得到其倍数 if(primeArr.get(j) * i > rightMargin){//防止越界 break; } stateArr[primeArr.get(j) * i] = true;//过滤掉当前倍数 //只让合数乘到为其因子的第一个质数,补充其他质数漏乘的同时获得下一阶倍数同时防止出现乘积重复 if(i % primeArr.get(j) == 0){ break; } } if(!stateArr[i]) { System.out.print(i + " "); } } }
二,快速判断long范围内整数是否为质数:
费马小定理+二次探测优化:
这个内容太多了,,建议直接csdn或去看相关的算法课,我就不多解释了
static class JudgeLargePrimer{//判断一个数是否为质数(long 内) //以下为费马小定理及其二次探测的优化算法 private static boolean flag; private static long gcd(long a, long b){ if(b == 0) return a; return gcd(b, a % b); } private static long fastMul(long x, long y, long mod){ if(y == 1) return x % mod; if((y & 1) > 0) return ((x % mod) * fastMul(x, y - 1, mod)) % mod; long u = fastMul(x, y >> 1, mod); long v = (u * u) % mod; if(v == 1){ if(u != 1 && u != mod - 1){ flag = false; } } return v; } public static boolean FermatSmallTheorem(long x){ if(x <= 1) return true; int correctionTimes = 30; flag = true; while(correctionTimes-- > 0){ Random random = new Random(); long sampleData = (long) random.nextInt() % 10000 * random.nextInt() + 1; while(gcd(sampleData, x) != 1){ if(sampleData < x || sampleData % x != 0){ return false; } sampleData = (long) random.nextInt() % 1000 * random.nextInt() + 1; } if(fastMul(sampleData, x - 1, x) != 1 || !flag) return false; } return true; } }
测试输出:
这里稍微提一下,费马小定理的逆定理需要随机数辅助来判断,有概率错误,不过加上二次探测优化后概率小的离谱,最大不超过4^-1000(4的负1000次方),所以放心用,,
完整的调试代码:
/** * @author 阿白 * @version v1.8 * 希望毕业能直接找到工作,,, * */ import java.util.*; public class HelloWorld { public static void main(String[] a) { PrimeTest.shifterWayFindPrimeTest(); PrimeTest.judgeBigNumberIsPrime(); } } class PrimeTest{ public static void shifterWayFindPrimeTest(){ Scanner sc = new Scanner(System.in); System.out.println("埃氏筛:"); System.out.print("请输入要找质数的右边界:"); int rightMargin1 = sc.nextInt(); PrimerArithmetic.FindSectionPrimer.EhrlichSieve(rightMargin1); System.out.println("\n欧拉筛:"); System.out.print("请输入要找质数的右边界:"); int rightMargin2 = sc.nextInt(); PrimerArithmetic.FindSectionPrimer.EulerSieve(rightMargin2); } public static void judgeBigNumberIsPrime(){ Scanner sc = new Scanner(System.in); System.out.print("\n请输入判断的大整数(2E18以内),按Ctrl + D 结束输入: "); while(sc.hasNextLong()){ long targetNumber = sc.nextLong(); boolean sign = PrimerArithmetic.JudgeLargePrimer.FermatSmallTheorem(targetNumber); if(sign){ System.out.println("No"); }else{ System.out.println("Yes"); } System.out.print("\n请输入判断的大整数(9E18以内): "); } } } class PrimerArithmetic{ static class FindSectionPrimer{//找范围内的所有质数 public static void EhrlichSieve(int rightMargin){//埃氏筛 if(rightMargin < 2) { System.out.println("这个区间内没有质数!"); return; } System.out.println("0 到 " + rightMargin + " 的质数有:"); boolean[] stateArr = new boolean[rightMargin + 1]; for(int i = 2; i <= rightMargin; i++){ if(!stateArr[i]){ System.out.print(i + " "); for(int j = i; j <= rightMargin; j += i){ stateArr[j] = true; } } } } public static void EulerSieve(int rightMargin){//欧拉筛(线性筛) if(rightMargin < 2) { System.out.println("这个区间内没有质数!"); return; } System.out.println("0 到 " + rightMargin + " 的质数有:"); boolean[] stateArr = new boolean[rightMargin + 1]; ArrayList<Integer>primeArr = new ArrayList<>(); for(int i = 2; i <= rightMargin; i++){ if(!stateArr[i]) {//当前数有没有被过滤掉(是否为质数) primeArr.add(i);//如果是质数,放入数组备用 } for(int j = 0; j <= primeArr.toArray().length; j++){//从2开始,将没有过滤掉的数与已有质数相乘得到其倍数 if(primeArr.get(j) * i > rightMargin){//防止越界 break; } stateArr[primeArr.get(j) * i] = true;//过滤掉当前倍数 //只让合数乘到为其因子的第一个质数,补充其他质数漏乘的同时获得下一阶倍数同时防止出现乘积重复 if(i % primeArr.get(j) == 0){ break; } } if(!stateArr[i]) { System.out.print(i + " "); } } } } static class JudgeLargePrimer{//判断一个数是否为质数(long 内) //以下为费马小定理及其二次探测的优化算法 private static boolean flag; private static long gcd(long a, long b){ if(b == 0) return a; return gcd(b, a % b); } private static long fastMul(long x, long y, long mod){ if(y == 1) return x % mod; if((y & 1) > 0) return ((x % mod) * fastMul(x, y - 1, mod)) % mod; long u = fastMul(x, y >> 1, mod); long v = (u * u) % mod; if(v == 1){ if(u != 1 && u != mod - 1){ flag = false; } } return v; } public static boolean FermatSmallTheorem(long x){ if(x <= 1) return true; int correctionTimes = 30; flag = true; while(correctionTimes-- > 0){ Random random = new Random(); long sampleData = (long) random.nextInt() % 10000 * random.nextInt() + 1; while(gcd(sampleData, x) != 1){ if(sampleData < x || sampleData % x != 0){ return false; } sampleData = (long) random.nextInt() % 1000 * random.nextInt() + 1; } if(fastMul(sampleData, x - 1, x) != 1 || !flag) return false; } return true; } } }
Java学习笔记 第六天 质数查找与较大整数的质数判断
于 2022-05-27 23:30:08 首次发布