闲来无事,突然想起某群的加入条件是计算第5千万个素数是多少,如果按照素数定义来写程序,起码要跑好几个小时,所以观察了一下素数的规律,对计算方法进行了优化
- 话不多说,直接上代码
import java.util.ArrayList; import java.util.Date; /** * Created by dlh on 2020/4/27. */ public class test { /** * 原始筛选素数的方法 * @param num 遍历范围 * @return 当前范围类所有的素数 */ public static ArrayList<Integer> getSS(Integer num){ Date startDate = new Date(); ArrayList array = new ArrayList<Integer>(); int i,j; for (i=2;i<=num;i++){ //外循环进行遍历素数范围 for(j=2;j<i;j++) //内循环用于判断外循环的值是否为素数 if(i%j==0) //判断在1-i之间是否存在能让i整除的数 break; //如果存在就代表,不是素数,7a686964616fe78988e69d8331333433623163反之则是 if (j==i){ //如果j==i,则证明在1-i之间没有数能整除i,说明i是一个素数 array.add(i); //将i存放进数组 } else continue; //如果不相等,则继续外循环下一个数 } Date endDate = new Date(); long runtime = startDate.getTime()-endDate.getTime(); System.out.println("原始方法用时时间"+runtime); return array; } /**第二步优化,使用ArrayList存在类型转换的开销 * @param num 遍历范围 * @return 当前范围类所有的素数 */ public static ArrayList<Integer> getSS2(Integer num){ Date startDate = new Date(); ArrayList array = new ArrayList<Integer>();//素数数组 array.add(2); int i,j; for (i=2;i<=num;i++){ //外循环进行遍历素数范围 for(j=0;j<array.size();j++) { //内循环用于判断外循环的值是否为素数 Integer cs = (Integer) array.get(j); if (i % cs == 0) //判断在1-i之间是否存在能让i整除的数 break; //如果存在就代表,不是素数 if ( j == (array.size()-1) ) { //除到最后一个数了,还没有被除断,则为素数 array.add(i); //将i存放进数组 } else continue; //如果不相等,则继续外循环下一个数 } } Date endDate = new Date(); long runtime = endDate.getTime()-startDate.getTime(); System.out.println("改进方法用时时间"+runtime); return array; } /** 第三步优化,直接采用int数组,减少类型转换开销 * @param num 需要获取素数的个数 * @return 指定个数的 素数数组 */ public static int[] getSS3(Integer num){ Date startDate = new Date(); int[] array = new int[num];//素数数组 int dq_index = 1;//初始下标 array[0]=2;//默认第一个元素 int i,j; for (i=2;i<=Integer.MAX_VALUE;i++){ //外循环进行遍历素数范围 if (dq_index==num) break; //当数组到达指定长度时,退出循环 for(j=0;j<dq_index;j++) { //内循环用于判断外循环的值是否为素数 int cs = array[j]; if (i % cs == 0) //判断在1-i之间是否存在能让i整除的数 break; //如果存在就代表,不是素数 if ( j == (dq_index-1) ) { //除到最后一个数了,还没有被除断,则为素数 array[dq_index]=i; //将i存放进数组 dq_index++;//数组有效长度自增 } else continue; //如果不相等,则继续外循环下一个数 } } Date endDate = new Date(); long runtime = endDate.getTime()-startDate.getTime(); System.out.println("筛选的最大值:"+i); System.out.println("========================"); System.out.println("改进方法用时时间"+runtime); return array; } public static int[] getSS4(Integer num){ long start = System.currentTimeMillis(); int[] array = new int[num];//素数数组 int dq_index = 2;//初始下标 array[0]=2;//默认第一个元素 array[1]=3;//默认第一个元素 int i,j; for (i=3;i<=Integer.MAX_VALUE;i+=2){ //外循环进行遍历素数范围 if (dq_index==num) break; //当数组到达指定长度时,退出循环 for(j=0;j<dq_index;j++) { //内循环用于判断外循环的值是否为素数 int cs = array[j]; if (i % cs == 0) //判断在1-i之间是否存在能让i整除的数 break; //如果存在就代表,不是素数 if ( j == (dq_index-1) ) { //除到最后一个数了,还没有被除断,则为素数 array[dq_index]=i; //将i存放进数组 dq_index++;//数组有效长度自增 } else continue; //如果不相等,则继续外循环下一个数 } } Date endDate = new Date(); long runtime = System.currentTimeMillis() - start; System.out.println("筛选的最大值:"+i); System.out.println("========================"); System.out.println("改进方法用时时间"+runtime); return array; } public static void main(String[] args) { int num = 500000; // List a1 = getSS(num); // System.out.println(a1.size()); ArrayList a2 = getSS2(7368788); Integer aLength = a2.size(); Integer lastSS = (Integer) a2.get((aLength-1)); System.out.println("素数个数:"+aLength+"======最后一个素数:"+lastSS); // int[] a2 = getSS3(num); // Integer aLength = a2.length; // Integer lastSS = a2[aLength-1]; // System.out.println("素数个数:"+aLength+"======最后一个素数:"+lastSS); } }
在2~1百万数字计算用时对比中,基本能提高10倍的性能,计算数字越多,性能提升越明显;
运算结果如下
原始方法用时时间-112538
78498
改进方法用时时间-13839
78498
78498为素数个数,计算结果也完全一致
方法2和方法3运行结果对比
- 方法2运行结果
改进方法用时时间574461
素数个数:500000======最后一个素数:7368787
2.方法3运行结果
筛选的最大值:7368788
========================
改进方法用时时间468286
素数个数:500000======最后一个素数:7368787
减少类型转换开销之后,同样从2~7368788数字之间查找,也能提示100s的速度;
3.方法4运行结果
筛选的最大值:7368789
========================
改进方法用时时间183045
素数个数:500000======最后一个素数:7368787
所有素数都是奇数,所以方法四就将循环步进改成了2,结果耗时又减少了60%多