Как эффективно рассчитать наибольший простой множитель длинного числа? - PullRequest
0 голосов
/ 19 января 2020

Я попытался создать программу Java для вычисления наибольшего простого множителя из любого длинного числа (в данном случае 600851475143). Когда я пытаюсь запустить его, программа компилируется бесконечно, без выдачи предупреждений или результата. Я понимаю, что, возможно, есть более простые / более простые способы решения этой проблемы, но мне любопытно, почему эта проблема не работает. Я не думаю, что сама логика c неверна, возможной ошибкой может быть использование длинных переменных (раньше я их не часто использовал).

Я объявил некоторые переменные так долго, чтобы они могли пространство, которое будет увеличено до «длинного» размера


    public class LargestPrimeFactor {
    public static void main(String []args){

        long num = 600851475143L;
        long largestPrimeFactor = 0L;
        boolean flag = false;

        //Find all factors of num
        for (long i = 2L; i <= num/2; i++){

            //If a number i is a factor of num
            if((num % i) == 0){

                //Find if the factor i is a prime number (only divisible by 1 and by itself)
                //by checking whether dividing it by any number results in an integer
                for (long j = 2L; j <= i/2; j++){

                    if (i/j == 0){

                        flag = true;
                    }
                    if (!flag){

                        if (i > largestPrimeFactor){

                            largestPrimeFactor = i;
                        }
                    }
                }
            }
        }
        System.out.print(largestPrimeFactor);
    }
}

Ответы [ 4 ]

1 голос
/ 20 января 2020

Это конкретно не решает все проблемы в вашем алгоритме, но может дать некоторые рекомендации. Ваше заданное число должно учитываться очень быстро, так как его основные факторы очень близки по величине. Это ускоряет процесс, поскольку целевое число уменьшается быстрее при обнаружении других факторов.

Рассмотрим следующие примеры. Первое значение - ваше, следующее - намного больше, а третье - наименьшее (и последнее по причине).

Вывод выглядит следующим образом: Фактически, это ваш номер. Часть adding - это новый фактор, часть continuing - это то, что остается после деления на этот фактор и что подлежит дальнейшей факторизации. Значения в скобках являются найденными факторами для данного числа.

Checking: 600851475143
Adding 71, continuing with 8462696833
Adding 839, continuing with 10086647
Adding 1471, continuing with 6857
Adding 6857, continuing with 1
[71, 839, 1471, 6857]

В результате первые два числа вычисляются очень быстро. Третий займет много времени. Это потому, что он прост, и используя этот метод, я должен был бы сгенерировать все простые числа до этого значения, чтобы проверить этот факт. Таким образом, не размер является единственным фактором (предназначенным для каламбура), это относительная величина основных факторов.

Вот процедура теста.

        for (long test : new long[] { 600851475143L,
                14385829455476874L,  300851475157L }) {
            System.out.println();
            System.out.println("Checking: " + test);
            List<Long> factors = findFactors(test);
            System.out.println(factors);
        }

    static private int lastReturnedPrimeIdx = 0;
    static private List<Long> primes = new ArrayList<>(
            List.of(2L, 3L));

   // find all prime factors in a supplied number.
    public static List<Long> findFactors(long n) {
        List<Long> factors = new ArrayList<>();
        lastReturnedPrimeIdx = 0;
        while (n > 1) {
            long p = nextPrime();
            while (n % p == 0) {
                factors.add(p);
                n /= p;
                System.out.println("Adding " + p
                        + ", continuing with " + n);
            }
        }
        return factors;
    }

    // Get the next prime. This memoizes the primes as they are computed.
    // Future tests on the same run can thus take advantage of the cached values.
    // Prime are computed in bulk.
    private static long nextPrime() {
        if (lastReturnedPrimeIdx < primes.size()) {
            return primes.get(lastReturnedPrimeIdx++);
        }

        // start where the it left off last time.
        long candidate = primes
                .get(lastReturnedPrimeIdx - 1);
        long max = primes.size() + 1_000; // generate 1000 more primes.

        outer:
        while (primes.size() < max) {
            candidate += 2;
            long bound = (long)Math.sqrt(candidate);
            for (int i = 0; i < primes.size(); i++) {
                long p = primes.get(i);
                if (candidate % p == 0 ) {
                    continue outer;
                }
                if (p > bound) {
                    break;
                }

            }
            primes.add(candidate);
        }
        return (primes.get(lastReturnedPrimeIdx++));
    }
}

Последнее замечание: я рекомендую вычислять простые числа будущих кандидатов по формуле:

  1. Деление только кандидатов на уже найденные простые числа .
  2. Проверка только нечетных кандидатов после 2.
  3. Проверка только primes до квадрата root кандидата.

Другой вариант - Сито Эратосфена

1 голос
/ 19 января 2020

Определенно, ваш код не будет работать бесконечно. Просто ваш код неэффективен, и поэтому выводится слишком много времени. Если вы тестируете меньшее число или используете эффективный код, вы получите результат.

Ниже приведен эффективный способ сделать это:

public class Main {
    public static void main(String[] args) {
        long num = 600851475143L;
        long divisor = 2, largestPrimeFactor;
        while (num != 0) {
            if (num % divisor != 0) {
                divisor++;
            } else {
                largestPrimeFactor = num;
                num /= divisor;
                if (num == 1) {
                    System.out.println("The largest prime factor: " + largestPrimeFactor);
                    break;
                }
            }
        }
    }
}

Вывод:

The largest prime factor: 6857

В вашем коде также есть следующие логические проблемы:

  1. Переменная flag была объявлена ​​вне внешнего l oop, что означает, что она будет никогда не сбрасывайте на false, как только оно станет true.
  2. Вместо проверки i / j == 0 вам необходимо проверить i % j == 0.
  3. Вы должны break внутренний l oop, как только i % j == 0.
  4. Кроме того, чек на largestPrimeFactor необходимо переместить с внутреннего l oop на внешний.

Кстати, ваш тест на простоту тоже не эффективный. Вместо проверки до половины числа достаточно проверки до квадрата root от числа. Проверьте https://en.wikipedia.org/wiki/Primality_test для более подробной информации. Ниже приведен эффективный код для проверки простоты:

static boolean isPrime(int number) {
    boolean prime = true;
    if (number == 0 || number == 1 || number == -1)
        return false;
    for (int i = 2; i <= Math.sqrt(number); i++) {
        if (number % i == 0) {
            prime = false;
            break;
        }
    }
    return prime;
}
0 голосов
/ 19 января 2020

Поиск факторов - очень трудоемкая операция.

Следовательно, при выполнении таких вычислений компьютер обычно занимает очень много времени.

Время выполнения вычислений можно сократить, сократив количество итераций.

Например, можно избежать итераций для всех четных чисел, превышающих 2, поскольку они не могут быть простыми.

Вот ссылка на рабочий код, найденный на Github:

https://gist.github.com/joseporiol/8559440

0 голосов
/ 19 января 2020

Похоже, что ваша программа прекрасно компилируется, но попадает в бесконечный (или очень длинный) цикл. Если вы поставите System.out.println("program started"); в начале вашего main метода, вы, вероятно, увидите его отображенным.

Также, если вы понизите long num, вы увидите, что ваш метод заканчивается.


РЕДАКТИРОВАТЬ: у вас есть два вложенных цикла. Первый выполняется num / 2 раза, а другой (num / 2) /2.

Если я не ошибаюсь, это означает, что он будет l oop (num ^ 2) / 8 раз. Что для long num = 600851475143L; это много. Следовательно, ваше приложение застревает итерацию более чем 4.51 × 10^22 раз

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...