You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

咨询:求600851475143最大质因数的更高效优雅解法

我已通过以下暴力解法代码成功求出数字600851475143的最大质因数,但深知该方法为暴力解法,想必存在更高效、更优雅的解决方案。我暂时未能构思出优化方案,希望有人能分享带有详细解释的解法。代码片段如下:

public class Solution { 
    // find prime numbers 
    ArrayList<Long> primelist = new ArrayList<>(); 
    ArrayList<Long> findPrime(Long num) { 
        for (Long i = Long...

嘿,很高兴你已经用暴力解法搞定了这个问题!确实,对于像600851475143这样的大数,暴力枚举所有可能的质数效率会极低——不仅要生成大量质数,还要逐个去试除,时间复杂度太高。下面我来分享两种更高效的解法,从基础优化到进阶的大数分解算法,都附带详细解释:

一、试除法的优化版本(适合中等大小的数)

暴力试除法的核心问题是做了太多无用功,我们可以通过几个关键优化大幅提升效率:

  1. 先处理偶数:先把目标数中所有的2都除干净,这样后续只需要遍历奇数,减少一半的循环次数。
  2. 只遍历到√n:如果一个数n有大于√n的因数,那对应的另一个因数必然小于√n,所以我们只需要遍历到当前n的平方根即可。
  3. 动态更新目标数:每次找到一个质因数后,就把目标数除以这个因数,缩小后续遍历的范围,同时记录当前最大的质因数。

优化后的Java代码:

public class LargestPrimeFactor {
    public static long findLargestPrimeFactor(long num) {
        long largestPrime = 0;

        // 先处理所有的2
        while (num % 2 == 0) {
            largestPrime = 2;
            num /= 2;
        }

        // 现在num是奇数,从3开始遍历,每次加2
        for (long i = 3; i <= Math.sqrt(num); i += 2) {
            // 当i是num的因数时,不断除尽
            while (num % i == 0) {
                largestPrime = i;
                num /= i;
            }
        }

        // 如果最后剩下的num大于2,说明它本身就是一个质数
        if (num > 2) {
            largestPrime = num;
        }

        return largestPrime;
    }

    public static void main(String[] args) {
        long target = 600851475143L;
        System.out.println("最大质因数是:" + findLargestPrimeFactor(target));
    }
}

代码解释:

  • 第一步先把所有的2都除完,这样后续的循环只需要处理奇数,避免了对偶数的无效判断。
  • 循环从3开始,每次加2,直到当前i的平方大于剩余的num。每次找到能整除num的i时,就把num除以i直到不能整除,同时更新最大质因数。
  • 最后如果剩余的num大于2,说明这个数本身就是一个质数(因为所有小于它的因数都已经被除尽了),它就是最大的质因数。

这个方法的时间复杂度降到了O(√n),对于600851475143来说,√n大概是775000左右,遍历起来非常快,比暴力枚举质数高效太多。

二、Pollard's Rho算法(针对超大数的高效分解)

如果遇到比600851475143大得多的数(比如10^20甚至更大),试除法的效率还是不够。这时候可以用Pollard's Rho算法,这是一种专门用于大数分解的概率性算法,速度比试除法快几个数量级。

Java实现示例:

import java.util.Random;

public class PollardsRho {
    private static final Random random = new Random();

    // 快速乘法,避免溢出
    private static long mul(long a, long b, long mod) {
        return (a * b - (long) ((double) a / mod * b) * mod + mod) % mod;
    }

    // 快速幂
    private static long pow(long a, long b, long mod) {
        long result = 1;
        a %= mod;
        while (b > 0) {
            if (b % 2 == 1) {
                result = mul(result, a, mod);
            }
            a = mul(a, a, mod);
            b /= 2;
        }
        return result;
    }

    // Miller-Rabin素性测试
    private static boolean isPrime(long n) {
        if (n <= 1) return false;
        if (n <= 3) return true;
        if (n % 2 == 0) return false;

        // 把n-1写成d*2^s
        long d = n - 1;
        int s = 0;
        while (d % 2 == 0) {
            d /= 2;
            s++;
        }

        // 测试几个底数,这里选2,3,5,7,11
        long[] bases = {2, 3, 5, 7, 11};
        for (long a : bases) {
            if (a >= n) continue;
            long x = pow(a, d, n);
            if (x == 1 || x == n - 1) continue;
            for (int j = 1; j < s; j++) {
                x = mul(x, x, n);
                if (x == n - 1) break;
            }
            if (x != n - 1) return false;
        }
        return true;
    }

    // Pollard's Rho算法找因数
    private static long pollardsRho(long n) {
        if (n % 2 == 0) return 2;
        if (n % 3 == 0) return 3;
        if (n % 5 == 0) return 5;

        while (true) {
            long c = random.nextInt((int) (n - 1)) + 1;
            java.util.function.LongUnaryOperator f = (x) -> mul(x, x, n) + c;
            long x = 2, y = 2, d = 1;

            while (d == 1) {
                x = f.applyAsLong(x);
                y = f.applyAsLong(f.applyAsLong(y));
                d = gcd(Math.abs(x - y), n);
            }

            if (d != 1 && d != n) return d;
        }
    }

    // 最大公约数
    private static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    // 递归分解并找最大质因数
    private static long findLargestPrime(long n) {
        if (n == 1) return 1;
        if (isPrime(n)) return n;
        long d = pollardsRho(n);
        return Math.max(findLargestPrime(d), findLargestPrime(n / d));
    }

    public static void main(String[] args) {
        long target = 600851475143L;
        System.out.println("最大质因数是:" + findLargestPrime(target));
    }
}

算法解释:

  1. Miller-Rabin素性测试:这是一种概率性的素数判断方法,能快速判断一个大数是否为质数,避免了试除法中逐个判断的低效。
  2. Pollard's Rho算法:通过构造伪随机函数来寻找大数的非平凡因数,利用 Floyd 循环查找算法来检测循环,从而找到因数。
  3. 递归分解:找到一个因数后,递归分解这个因数和原数除以该因数的结果,最终得到所有质因数中的最大值。

这种方法对于超大数的分解效率极高,即使是10^100级别的数也能快速处理。

内容的提问来源于stack exchange,提问作者mendokusai

火山引擎 最新活动