一、概念介绍
大家中学都学过,就不过多介绍了,大致提两点:
- 质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。
- 0和1既不是质数也不是合数,最小的质数是2
二、方法介绍
1.最直观,但效率最低的写法
-
public static boolean isPrime(int n){
-
if (n <=
3) {
-
return n >
1;
-
}
-
for(
int i =
2; i < n; i++){
-
if (n % i ==
0) {
-
return
false;
-
}
-
}
-
return
true;
-
}
这里特殊处理了一下小于等于3的数,因为小于等于3的自然数只有2和3是质数。
然后,我们只需要从2开始,一直到小于其自身,依次判断能否被n整除即可,能够整除则不是质数,否则是质数。
2.初步优化
假如n是合数,必然存在非1的两个约数p1和p2,其中p1<=sqrt(n),p2>=sqrt(n)。由此我们可以改进上述方法优化循环次数。如下:
-
public static boolean isPrime(int n) {
-
if (n <=
3) {
-
return n >
1;
-
}
-
int sqrt = (
int)Math.sqrt(n);
-
for (
int i =
2; i <= sqrt; i++) {
-
if(n % i ==
0) {
-
return
false;
-
}
-
}
-
return
true;
-
}
3.继续优化
我们继续分析,其实质数还有一个特点,就是它总是等于 6x-1 或者 6x+1,其中 x 是大于等于1的自然数。
如何论证这个结论呢,其实不难。首先 6x 肯定不是质数,因为它能被 6 整除;其次 6x+2 肯定也不是质数,因为它还能被2整除;依次类推,6x+3 肯定能被 3 整除;6x+4 肯定能被 2 整除。那么,就只有 6x+1 和 6x+5 (即等同于6x-1) 可能是质数了。所以循环的步长可以设为 6,然后每次只判断 6 两侧的数即可。
-
public static boolean isPrime(int num) {
-
if (num <=
3) {
-
return num >
1;
-
}
-
// 不在6的倍数两侧的一定不是质数
-
if (num %
6 !=
1 && num %
6 !=
5) {
-
return
false;
-
}
-
int sqrt = (
int) Math.sqrt(num);
-
for (
int i =
5; i <= sqrt; i +=
6) {
-
if (num % i ==
0 || num % (i +
2) ==
0) {
-
return
false;
-
}
-
}
-
return
true;
-
}
对于输入的自然数 n 较小时,也许效果不怎么明显,但是当 n 越来越大后,该方法的执行效率就会越来越明显了。
**另外一种解释**
**定义:约数只有1和本身的整数称为质数,或称素数。** **计算机或者相关专业,基本上大一新生开始学编程都会接触的一个问题就是判断质数,下面分享几个判断方法,从普通到高效。**
1)直观判断法
最直观的方法,根据定义,因为质数除了1和本身之外没有其他约数,所以判断n是否为质数,根据定义直接判断从2到n-1是否存在n的约数即可。C++代码如下:
-
bool isPrime_1( int num )
-
{
-
int tmp =num-
1;
-
for(
int i=
2;i <=tmp; i++)
-
if(num %i==
0)
-
return
0 ;
-
return
1 ;
-
}
2)直观判断法改进
-
bool isPrime_2( int num )
-
{
-
int tmp =
sqrt( num);
-
for(
int i=
2;i <=tmp; i++)
-
if(num %i==
0)
-
return
0 ;
-
return
1 ;
-
}
3)另一种方法
方法(2)应该是最常见的判断算法了,时间复杂度O(sqrt(n)),速度上比方法(1)的O(n)快得多。最近在网上偶然看到另一种更高效的方法,暂且称为方法(3)吧,由于找不到原始的出处,这里就不贴出链接了,如果有原创者看到,烦请联系我,必定补上版权引用。下面讲一下这种更快速的判断方法;
首先看一个关于质数分布的规律:大于等于5的质数一定和6的倍数相邻。例如5和7,11和13,17和19等等;
-
bool isPrime_3( int num )
-
{
-
//两个较小数另外处理
-
if(num ==
2|| num==
3 )
-
return
1 ;
-
//不在6的倍数两侧的一定不是质数
-
if(num %
6!=
1&&num %
6!=
5)
-
return
0 ;
-
int tmp =
sqrt( num);
-
//在6的倍数两侧的也可能不是质数
-
for(
int i=
5;i <=tmp; i+=
6 )
-
if(num %i==
0||num %(i+
2)==
0 )
-
return
0 ;
-
//排除所有,剩余的是质数
-
return
1 ;
-
}
-
#include <iostream>
-
#include <string>
-
#include <ctime>
-
#include <vector>
-
using
namespace
std;
-
bool isPrime_1( int num );
-
bool isPrime_2( int num );
-
bool isPrime_3( int num );
-
int main()
-
{
-
int test_num =
400000;
-
int tstart ,tstop;
//分别记录起始和结束时间
-
//测试第一个判断质数函数
-
tstart=clock ();
-
for(
int i=
1;i <=test_num; i++)
-
isPrime_1(i );
-
tstop=clock ();
-
cout<<
"方法(1)时间(ms):" <<tstop- tstart<<
endl ;
//ms为单位
-
//测试第二个判断质数函数
-
tstart=clock ();
-
for(
int i=
1;i <=test_num; i++)
-
isPrime_2(i );
-
tstop=clock ();
-
cout<<
"方法(2)时间(ms):" <<tstop- tstart<<
endl ;
-
//测试第三个判断质数函数
-
tstart=clock ();
-
for(
int i=
1;i <=test_num; i++)
-
isPrime_3(i );
-
tstop=clock ();
-
cout<<
"方法(3)时间(ms):" <<tstop- tstart<<
endl ;
-
cout<<
endl ;
-
system(
"pause" );
-
return
0 ;
-
}
运行结果如下;