求某一范围内纯质数–优化算法(java)
话不多说上题目(2021年蓝桥杯国赛javaB组)
一般来说正常思路都是先判断是否是质数,然后判断是否是纯质数,因为纯质数的每个位置上只有2,3,4,7这四种可能,所以耗时最长的是判断是否是质数,一般算法都是从2开始判断到n(n*n<=要判断的数)能否整除要判断的数,如下所示(该代码为网上随意找的,思路差不多。):
public class Test1 {
static int number=20210605;//测试的上限
static int map[]=new int[number];
static ArrayList<String> ans=new ArrayList();
static int anscount=0;
public static void main(String[] args) {
int count=0;
//此for循环找出范围内所有的质数
for (int i = 2; i <= number; i++) {
boolean add=true;
for (int j = 2; j*j <= i; j++) {
if(i%j==0) {
add=false;
continue;
}
}
if(add==true) {
map[count]=i;
count++;
}
}
// System.out.println("wanbi");
//此for循环对每个质数进行判断
for (int i = 0; i < count; i++) {
String single = String.valueOf(map[i]);
// System.out.print(single+" ");
boolean shi=true;
//将这个质数转化为字符串后将其的每一个字符转化为数字
flag: for (int j = 0; j < single.length(); j++) {
Integer wei = Integer.valueOf(single.charAt(j)-48);
//排除1和0
if(wei==1 || wei==0) {
shi=false;
break;
}
//判断这个位数是不是质数
for (int k = 2; k*k <=wei; k++) {
if(wei%k==0) {
shi=false;
break flag;
}
}
// System.out.print(wei+" ");
}
//如果这个质数是纯质数的话,总和+1
if(shi==true) {
System.out.println(single);
ans.add(single);
anscount++;
}
// System.out.println();
}
//调试时的输出语句
// for (int i = 0; i < count; i++) {
// System.out.print(map[i]+" ");
// }
// System.out.println();
// for (int i = 0; i < ans.size(); i++) {
// System.out.print(ans.get(i)+" ");
// }
System.out.println();
System.out.println(anscount);
}
}
经过运行发现就算只是查到20210605这一数字,也要运行大概十几分钟。当要查的数字增大时,该时间会更大。
下面我们对算法进行优化,首先,所有的偶数(2除外)都不会是质数,所以我们可以将外层循环从3开始,步进为2,这样可以减少一半的循环。但是效率还不是很高。
所以我们只能对判断质数的算法进行下手。
先解释一下为什么只判断到n(n*n<=要判断的数(i代替)):
然后对算法进行转化:
基于上述结论,将求质数的算法进行调整:
int numb = 20210605;
//存放已经找到的质数
ArrayList<Integer> list = new ArrayList<>();
//因为2也是纯质数,并且循环是从3开始,所以先将2放入集合
list.add(2);
for (int i = 3; i <= numb; i=i+2) {
boolean flag = true;
for (int j = 0 ;list.get(j)*list.get(j)<=i;j++) {
if (i%list.get(j)==0){
flag = false;
break;
}
}
if (flag){
list.add(i);
System.out.println(i);
}
}
在求质数的基础上添加判断纯质数的代码:
public class Test2 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(start);
int numb = 20210605;
int count = 0;
ArrayList<Integer> list = new ArrayList<>();
list.add(2);
//因为2也是纯质数,并且循环是从3开始,所以先加1
count++;
System.out.println(2);
for (int i = 3; i <= numb; i=i+2) {
boolean flag = true;
for (int j = 0 ;list.get(j)*list.get(j)<=i;j++) {
if (i%list.get(j)==0){
flag = false;
break;
}
}
if (flag){
list.add(i);
boolean matches = Pattern.matches("[2357]+", i + "");
if (matches){
System.out.println(i);
count++;
}
}
}
System.out.println("数量:"+count);
System.out.println("时间:"+(System.currentTimeMillis()-start));
}
}
}
}
System.out.println("数量:"+count);
System.out.println("时间:"+(System.currentTimeMillis()-start));
}
}
经验证原本的算法时间大概需要十几分钟,优化后的代码约为5秒钟。效率有很大的提升,但是算法是我自己进行的转化,数学上的严谨性有待验证,如有不妥之处,还请各位数学大神指点,或者论证一下算法是否可行。