本篇目录
前言
本文的题目来自于PAT平台,PAT (Basic Level) (中文) 1007 素数对猜想 ,实现语言为Java。其中涉及了“判断一个整数是否为素数”的算法,我决定记录下来。
一、题目要求
让我们定义dn为:dn=pn+1−pn,其中pi是第i个素数。显然有d1=1,且对于n>1有dn是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。
现给定任意正整数N
(<10^5),请计算不超过N
的满足猜想的素数对的个数。
输入格式:
输入在一行给出正整数N
。
输出格式:
在一行中输出不超过N
的满足猜想的素数对的个数。
输入样例:
20
输出样例:
4
二、思路分析
1.什么是素数对
根据已知信息,如果正整数 m 与正整数 (m+2) 都是素数(质数),那么这就是一个素数对。对于题目给定的计算范围 N ,要求 m+2 <=N ,这才能将这个素数对计算在内。
2.判断素数
要计算素数对的数量,其中必然要调用一个功能为“判断所给整数是否为素数”的函数。那么不妨先把这个函数实现。为了尽可能优化代码,首先分析一下素数(质数)都有哪些特点。
① 1不是素数也不是合数,不大于5的素数只有2 3 5 三个,其中包含了素数对(3,5)。当N≤5时这是比较特殊的情况,应当单独进行判断。
② 在大于5的数中,所有素数的个位数字都是1 3 7 9 中的一个。这可以通过取余运算来判断。
③ 在大于5的数中,所有的素数都只可能是(6n-1)或者(6n+1),其中n是正整数。(这条结论来自百度。)首先要判断所给的整数是否有可能是素数,如果所给数字不是(6n-1)或者(6n+1)那就一定不是素数。而对于可能的素数,就要通过因数分解法进一步判断。
④ 每个合数都能分解为素数的乘积
只要判断每个可能是素数的整数,是否是给定整数n的因数,就可以了。这里要注意,不用把从1到n的每一个整数都作为因数都判断一遍,只要 要判断的整数的乘方≤n 就可以了。
下面是判断给定整数是否为素数的代码,时间复杂度为O(√n)。
//判断一个整数是否是素数 时间复杂度为O(√n)
private static boolean ifsushu(int t){
//判断特殊值
if(t==2 || t==3 || t==5) return true;
//若尾数不是1 3 7 9 则一定不是素数(10以上的数)
if(t%2==0 || t%5==0) return false;
//若此数不是6n+1或者6n-1,则一定不是素数(10以上的数)
if((t+1)%6!=0 && (t-1)%6!=0) return false;
//遍历检查(合数一定能分解为质数的乘积)
for(int i=1;Math.pow(6*i-1,2)<=t;i++)
if(t % (6*i-1) == 0 || t % (6*i+1) == 0)
return false;
return true;
}
3.判断素数对
这一步就很容易了,只要在给定N的范围内,通过循环遍历每一个整数,用一个计数器t来记录素数对的数量,就可以了。不过,为了尽可能优化算法,对于N>5的情况,只对可能为素数的整数进行判断,其它的整数就不用算了。这一层循环的时间复杂度为O(n)。
要注意的是,在≤5的范围内还有一对(3,5)素数对,因此计数器的初始值要设置为1 而不是0 。
三、完整代码
算法整体的时间复杂度为O(n√n)。
import java.io.*;
class Main{ //给定正整数n,计算不超过n的素数对的数量 时间复杂度O(n√n)
public static void main(String[] args) throws IOException{
//接收输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
//特殊值
if(n < 5){ //若给定数字<5,直接打印0 然后返回
sop(0);
return;
}
//计算素数对的数量
int t = 1; //计数器,素数对个数(初始值为1是预加上3 5 这一对)
for(int i = 1;6*i+1<=n;i++)
if(ifsushu(6*i-1) && ifsushu(6*i+1) )
t++;
sop(t); //打印输出
}
//判断一个整数是否是素数 时间复杂度为O(√n)
private static boolean ifsushu(int t){
//判断特殊值
if(t==2 || t==3 || t==5) return true;
//若尾数不是1 3 7 9 则一定不是素数(10以上的数)
if(t%2==0 || t%5==0) return false;
//若此数不是6n+1或者6n-1,则一定不是素数(10以上的数)
if((t+1)%6!=0 && (t-1)%6!=0) return false;
//遍历检查(合数一定能分解为质数的乘积)
for(int i=1;Math.pow(6*i-1,2)<=t;i++)
if(t % (6*i-1) == 0 || t % (6*i+1) == 0)
return false;
return true;
}
//打印函数
private static void sop(int n){
System.out.println(n);
}
}
总结
通过这个练习,我进一步了解了素数以及素数的判断算法,有趣~