判断质数(Java版)

这篇文章介绍两种简单的判断质数的方法。

基础版

第一种就是根据质数的定义:质数是大于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:由于数据集不是很大,用基础版的优化也可以过

相关推荐
叼烟扛炮3 分钟前
C++第四讲:类和对象(下)
c++·算法·类和对象
Rabitebla4 分钟前
vector 的骨架:三根指针、模板陷阱与迭代器失效的第一现场
开发语言·数据结构·c++·算法
代码不停21 分钟前
BFS解决floodfill算法题目练习
算法·宽度优先
上弦月-编程28 分钟前
C语言指针从入门到实战
java·jvm·算法
WL_Aurora28 分钟前
Python 算法基础篇之树和二叉树
python·算法
txzrxz30 分钟前
关于前缀和
算法·动态规划·图论
杨连江32 分钟前
载流子矩阵限域束缚实现常温常压超导的理论与结构设计
算法
做cv的小昊40 分钟前
【TJU】研究生应用统计学课程笔记(6)——第二章 参数估计(2.4 区间估计)
人工智能·笔记·线性代数·算法·机器学习·数学建模·概率论
普贤莲花1 小时前
【2026年第18周---写于20260501】---舍得
程序人生·算法·leetcode
2zcode1 小时前
基于深度学习的口腔疾病图像识别系统(UI界面+改进算法+数据集+训练代码)
人工智能·深度学习·算法