判断质数(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:由于数据集不是很大,用基础版的优化也可以过

相关推荐
IT探索9 分钟前
Linux 查找文件指令总结
linux·算法
Lumos1861 小时前
51单片机从零到实战(完结)——后续学习路线建议
算法
稚南城才子,乌衣巷风流1 小时前
DFS序详解:原理、应用与实现
算法·深度优先·图论
稚南城才子,乌衣巷风流1 小时前
动态树(Dynamic Tree)数据结构详解
数据结构·算法
变量未定义~2 小时前
最小生成树1(Prim模板)、最小生成树2(kruskal模板)、最近公共祖先(模板)
算法
这就是佬们吗2 小时前
Python入门⑤-异常处理、文件操作与实战项目
开发语言·数据库·python·算法·pycharm
LONGZETECH2 小时前
新能源汽车充电设备装配与调试仿真教学软件 技术架构与核心实现解析
大数据·算法·3d·unity·架构·汽车
自学小白菜2 小时前
关于提升机器学习算法做预测回归任务精度的方法分享
算法·机器学习·回归·提升精度
学废了wuwu2 小时前
从入门到实战:深入浅出模型压缩三大核心——剪枝、彩票假设与知识蒸馏
人工智能·算法·剪枝
小肝一下2 小时前
3. 单链表
c语言·数据结构·c++·算法·leetcode·链表·dijkstra