欢迎访问宙启技术站
智能推送

Java函数编写:如何检查一个数是否为质数?

发布时间:2023-06-18 15:38:36

一个数是否为质数是一道经典的算法问题,通常是初学编程的学生们的第一个作业。在这里,我们将介绍几种不同的方法来检查一个数是否为质数,务求帮助初学者更好地理解。

方法一:暴力枚举

暴力枚举方法是最简单和直接的检查一个数是否为质数的方法。其思路就是检查所给定的数对于每一个比它小的数是否有因子。如果存在因子,其就不是质数,否则则为质数。

下面是基于暴力枚举的Java函数实现:

public static boolean isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

该函数中,我们首先判断输入的数是否小于2,如果是,则直接返回false。这是因为质数定义为大于等于2的自然数。然后我们从2开始枚举到n的平方根,判断是否有因子。如果存在因子则说明n不是质数,返回false;如果都不存在,则n为质数,返回true。

该方法最大的缺点是在检查大数时表现极为低效,因为其时间复杂度为O(n),而且该方法还不能够通过大数据测试。

方法二:优化的暴力枚举

为了提升暴力枚举的效率,我们可以进行一些简单的优化,如只枚举奇数,因为偶数都能被2整除。我们只需要排除2就可以缩小判断范围。此外,我们还可以通过对过去的质数列表进行较小因子的判断,来加速搜索。

下面是代码实现:

public static boolean isPrime(int n) {
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) return false;
    }
    return true;
}

在该函数中,我们将2作为特殊情况来处理,只检查大于等于3的奇数。此外,我们通过判断过去所有的质数的较小因子来优化搜索过程。

虽然该方法比直接暴力枚举略优,但是对于大数,依旧会导致较差的性能。

方法三:分解质因数

分解质因数是一种较好的方法,既可以检查质数,又可以输出一个数的所以的质因数。这里我们只考虑检查质数,具体实现思路如下:

对于给定的数n,如果存在一个小于1并且大于n的正整数d,那么n除以d的结果一定小于n且大于1,并且n除以d的商和余数分别是q和r,其中0 ≤ r < d。因此n就不能是质数。

根据这个思路,我们可以通过分解n的质因数来检查n是否是质数。如果n有大于sqrt(n)的因子,则一定有小于sqrt(n)的因子。

下面是代码实现:

public static boolean isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i <= Math.sqrt(n); i++) {
        if (n % i == 0) return false;
    }
    return true;
}

在该函数中,我们将n的因子枚举到sqrt(n),大于sqrt(n)的因子可以从小于sqrt(n)的因子中推导得出。

该方法的效率比简单的暴力枚举和优化的暴力枚举方法都优秀,但是其空间开销较大。因为分解n的质因数需要存储质数列表。

方法四:埃氏筛法

埃氏筛法是一种在指定区间内,通过不断的筛选质数,来输出输入区间中所有质数的高效算法。该方法适用于求解大量小于一个正整数n的质数。

在实现上,我们从2开始,将小于n的所有正整数标记为素数,然后依次遍历素数列表的每一个元素,将素数的倍数标记为合数。遍历结束后,剩下的未被标记为合数的数即为质数。

下面是基于埃氏筛法的Java实现:

public static List<Integer> sieve(int n) {
    List<Integer> primes = new ArrayList<>();
    boolean[] isPrime = new boolean[n + 1];
    Arrays.fill(isPrime, true);

    for (int i = 2; i * i <= n; i++) {
        if (isPrime[i]) {
            for (int j = i * i; j <= n; j += i) {
                isPrime[j] = false;
            }
        }
    }
    for (int i = 2; i <= n; i++) {
        if (isPrime[i]) {
            primes.add(i);
        }
    }
    return primes;
}

在该代码中,isPrime是一个数组,表示区间小于等于n的数是否是质数,初始所有元素都被认为是质数。如果一个元素是质数,则将其倍数标记为非质数。最后,所有未被标记为非质数的数即为质数。

该方法在处理小于10000的范围内,使用o(n log log n)的时间复杂度仍然是比较快的。但当n较大时,这个方法不再稳定表现,因为isPrime数组需要的空间和n成正比。

方法五:米勒-拉宾算法

毫不夸张的说,米勒-拉宾算法(Miller-Rabin primality test)是一个广泛被使用的判断质数的算法,尤其对于大数的判断具有很强的优势。

米勒-拉宾算法的基本思路是:对于一个正整数n,如果存在一个质数a,使a^(n-1) mod n != 1,则n一定不是质数,否则n可能是一个质数。

这看上去麻烦的算法其实好理解。它的原理便是使用费马小定理(Fermat's Little Theorem)的逆命题。该定理表述如下:

如果a是一个质数,且n是一个整数,那么(a^n)mod n = a mod n。

也就是说,如果a和n都是质数,当且仅当a^(n-1) mod n = 1时n是一个质数。米勒-拉宾算法便利用了这个定理进行验证。

下面是Java函数实现:

public static boolean isPrime(int n) {
    if (n < 2) return false;
    int r = 0;
    int d = n - 1;
    while ((d & 1) == 0) {
        r++;
        d >>= 1;
    }
    for (int a : new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23}) {
        if (n == a) {
            return true;
        }
        if (checkComposite(n, a, d, r)) {
            return false;
        }
    }
    return true;
}

在该函数中,