这篇文章介绍两种简单的判断质数的方法。
基础版
第一种就是根据质数的定义:质数是大于1的自然数中,除了1和它本身外没有其他因数的数。
所以我们只需要判断一下这个数能否被比其小且大于等于2的数整除就可以了。
实现代码如下:
java
public static boolean isPrime(int n) {
if (n <= 1) return false; // 1和负数不是质数
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return false; // 有其它因子
}
}
return true;
}
这种是最容易实现的也是最慢的方法,我们可以在其之上做一些优化:
1.如果这个数不能被2整除,那么就一定不会被2的倍数整除,所以循环时只需单独判断一下2的情况就可以只判断能否被奇数整除即可;
2.如果这个数 n ,在运算到**√n** 的时候还没有找到因数,后面就不需要找了。因为假设大于**√n** 的数是它的因数,那么它的另一个因数一定小于 √n ,这样的话,就不会出现到达 √n也没有找到因数的情况。
我们可以据此来优化我们的代码:
java
public static boolean isPrime(int n) {
if (n <= 1) return false;
if (n == 2) return true; // 2是质数
if (n % 2 == 0) return false; // 偶数不是质数(这种情况已经筛掉了2)
// 只需检查到 sqrt(n),且只检查奇数
int limit = (int) Math.sqrt(n);
for (int i = 3; i <= limit; i += 2) {
if (n % i == 0) {
return false;
}
}
return true;
}
就可以将判断一个质数的时间复杂度从O(n)优化到O(√n)。
但是有些时候,会出现需要大量判断一个范围内的质数的时候,这两种时间复杂度就会变成O(n*n)和O(n*√n),我们就需要用一种筛查的方式找到一个范围内的非素数,这样其他的就是素数,而且可以避免重复计算。
埃氏筛
我们如果要判断n以内的数是否为质数,我们可以先创建一个boolean类型的数组,用下标表示数字,将里面的合数都记录一下,这样没记录的就是质数。
如何记录合数呢,假设一个数是合数,那么它一定可以由一个比它小的数乘另外一个数得到,因此,我们只需要让这些较小的数变化自身的倍数即可。例如,我们找一下20以内的合数:
2的倍数:4、6、8、10、12、14、16、18、20
3的倍数:**6、**9、12、15、18
4的倍数:**8、12、**16、20(被2包含了)
5的倍数:10、15、20
......
其实我们不需要向下继续筛查了,因为5*5>20了,原理同我们刚才优化基础版的原理一样
于是,我们找到的合数有4、6、8、9、10、12、14、15、16、18、20
对应的,质数就有2、3、5、7、11、13、17、19
对于筛查下界我们还可以进行优化,目前是2 * i,例如在算3的倍数时第一个取到的是6,为3 * 2,这在我们对2筛查时就已经2 * 3筛查过了,因此对于 i 我们可以从i * i 开始筛查,因为如果比此数小就一定在前面筛过,例如 i *(i - 1) 一定在 i - 1中筛查过了(见前方加粗示例)
而且,对于4的倍数而言,是一定被2的倍数包含的,所以在进行筛查时可以先判断一下是否在合数中标记了次数,如果标记了是合数,说明前面有比它小的因数,那么就无需判断这个数(因为前面的因数一定将它要标记的数标记完成了)
故代码实现如下:
java
//n为数据的范围(上界)
boolean[] isComposite = new boolean[n + 1];
public static void setValue(int n) {
for (int i = 2; i * i <= n; i++) {
if (!isComposite[i]) {
// 从 i*i 开始标记(优化:比从 2*i 开始好)
for (int j = i * i; j <= n; j += i) {
isComposite[j] = true;
}
}
}
}
//由于0和1既不是质数也不是合数,如果需要判断质数可以先将他们取为true
但是我们其实不难发现,这种情况下其实示例中的12和18都被标记了两次,还是会有重复计算的位置,时间复杂度没有优化到O(n)实际上,埃氏筛的时间复杂度是O(n log log n),十分接近线性,但是要严格线性就需要使用欧拉筛,欧拉筛保证被最小质因子表示一次,所以是O(n),此篇不详细展开欧拉筛的方法。
小练习:筛法判断质数_牛客题霸_牛客网
tips:由于数据集不是很大,用基础版的优化也可以过