Почему этот алгоритм разложения на простые множители дал правильный ответ даже при наличии изъяна?

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

"Каков наибольший простой делитель числа 600851475143?"

Программа используется, чтобы найти ответ именно так с помощью C:

#include<math.h> // for remainder because % does not work with double or floats
#include<stdio.h>  
main()
{
    double x=600851475143,y=3.0;
    while(x!=y)                         // divide until only the number can divide itself
    {
        if(remainder((x/y),1)==0.0)     // if x is divisible by y which means it is a factor then do the magic
        {
            x=x/y;                      // divide the number x by y thereby reducing one constituent factor
        }
        y=y+2;                          // add 2 simply because only odd numbers can be prime and hence only prime numbers can be prime factors
    }
    printf("%lf",y);                    // do the printing magic
}

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

Удивительно, но ответ, который выдает эта программа, правильный, я проверил ответ.

Как мне это понять? Это не имеет смысла.


person xabhisan    schedule 14.05.2013    source источник
comment
Неправильный алгоритм не означает, что он всегда должен давать неправильный результат. Точно так же, как неопределенное поведение не требуется для сбоя программы. Кстати, вам лучше избегать использования чисел с плавающей запятой для решения задач, связанных с целыми числами. Используйте if (x % y == 0), чтобы проверить, делится ли x на y. И самое важное: отформатируйте код.   -  person    schedule 14.05.2013
comment
@ H2CO3: хорошее предложение, но учтите, что для 600851475143 потребуется 64-битное целое число.   -  person Paul R    schedule 14.05.2013
comment
@PaulR Что не так с 64-битными целыми числами? (Я имею в виду, что мы живем в 2013 году, есть C99 и int_least64_t, я стараюсь не беспокоиться о платформах, где они недоступны. Я хочу и ожидаю наличия 64-битных целых чисел.)   -  person    schedule 14.05.2013
comment
Ничего - просто использование ints не будет работать в этом случае на большинстве платформ, и OP может не знать об этом.   -  person Paul R    schedule 14.05.2013
comment
@PaulR А, значит, вы имеете в виду int, именно. Да, тогда то, что вы говорите, разумно. (Жаль, что большинство новичков не знают / не используют long long и т. Д.)   -  person    schedule 14.05.2013
comment
@PaulR OP знает, что int не может работать, поэтому он использовал double.   -  person xabhisan    schedule 14.05.2013
comment
@xabhisan: молодец, но было бы гораздо уместнее использовать int64_t, чем переключаться на операции с плавающей запятой, что влечет за собой целый ряд других проблем.   -  person Paul R    schedule 14.05.2013
comment
не должно быть x> y?   -  person Koushik Shetty    schedule 14.05.2013
comment
Есть еще проблемы с кодом: (1) 2 - простое число. (2) А как насчет факторов, которые следует учитывать дважды (например, для числа 27 простое число 3 следует пересчитать 3 раза)   -  person amit    schedule 14.05.2013
comment
@amit Именно то, о чем я хочу спросить!   -  person xabhisan    schedule 14.05.2013
comment
@xabhisan Ну, даже сломанные часы показывают правильное время дважды в день. Так делает этот алгоритм.   -  person amit    schedule 14.05.2013
comment
@amit Для правильности ответа должно быть логическое объяснение, это не может быть совпадением.   -  person xabhisan    schedule 14.05.2013


Ответы (4)


Обратите внимание, что в алгоритме есть 3 недостатка:

  1. 2 также простое число
  2. Он может делить числа, не являющиеся простыми, и нечетные (например, 9)
  3. Он НЕ делит простое число более одного раза (как и следовало ожидать для таких чисел, как 27).

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

  1. Номер входа нечетный (поэтому переход на 2 не имеет значения)
  2. Пусть число будет n = p1*p2*...*p_k, где p_i - все простые числа. Для каждого j!=i: p_i != p_j. Здесь это означает, что на самом деле каждое простое число является множителем входного числа только один раз, и, таким образом, проблемы 2 + 3 «избегаются». (Проблема 2 - это тривиально, почему ее избегают, проблемы 3 избегают, потому что вы уже разделили число со всеми соответствующими простыми множителями, поэтому для каждого m=p_i1*...*p_ic, поскольку число уже было разделено на все простые множители - p_i1,...p_ic - вы не сможете разделить это с m.
person amit    schedule 14.05.2013
comment
@Thomash Я не согласен. Например, для 27 - это даст вам ответ 9, что не является основным множителем. Я согласен с тем, что, решив вопрос (3), это не проблема. - person amit; 14.05.2013
comment
Вы ошибаетесь, утверждая, что метод работает тогда и только тогда, когда n=p₁*p₂*...*pₖ, где pᵢ - различные простые числа. Утверждение неверно в части только если, потому что метод работает для некоторых чисел не этой формы, например 1161 = 27 * 43 дает правильный результат 43. Этот пример показывает, что ваше утверждение неверно. - person James Waldby - jwpat7; 14.05.2013

Поскольку все говорят вам, что не так с вашей программой, я дам правильный алгоритм факторизации целых чисел с использованием пробного деления:

function factors(n)
    f, fs := 2, []
    while f * f <= n
        if n % f == 0
            append f to fs
            n := n / f
        else f := f + 1
    append n to fs
    return fs

Это решает две проблемы с вашим кодом. Во-первых, он правильно определяет факторы 2. Во-вторых, он возвращает все факторы с их множественностью.

Чтобы ответить на ваш вопрос о делении на не простые числа: это проблема производительности, а не проблема правильности. Поскольку пробные делители проверяются в возрастающем порядке, любые составные делители уже будут удалены из факторизуемого числа при проверке составляющих их простых чисел. Это означает, что деление на составное бесполезно, но на результат это не повлияет.

И, конечно, никогда не следует использовать арифметику с плавающей запятой при работе с целыми числами. В C, когда вы выходите за рамки длинных длинных целых чисел, вы, вероятно, захотите переключиться на библиотеку gmp.

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

person user448810    schedule 14.05.2013

Это работает, потому что в вашем номере нет повторяющихся факторов.

600851475143 = 71 * 839 * 1471 * 6857

Попробуйте, например, 1573499 (23 * 37 * 43 * 43).

person pmg    schedule 14.05.2013
comment
Я бы хотел, чтобы этот алгоритм блокировался при вводе 9. - person Vesper; 14.05.2013
comment
@Vesper точно! но на 225 он производит непростой множитель и останавливается. - person Will Ness; 14.05.2013

Если проверяемое число четное, программа будет работать вечно. x всегда будет четным, y всегда будет нечетным, поэтому x == y никогда не будет истинным.

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

Интересен случай, когда в проверяемом числе есть множители, представляющие собой квадраты, кубы и т. Д. Нечетных простых чисел. Цикл найдет нечетные простые множители и разделит на них, но, например, если 3 ^ 2 - множитель, он будет делиться на 3, оставляя множитель 3. Что именно происходит, зависит от простых множителей. Если есть только один простой квадратный множитель p ^ 2, программа не завершится. Если есть куб или два квадрата (p ^ 3 или p ^ 2 q ^ 2), результатом будет неправильный оставшийся множитель p ^ 2 или pq, если только число не имеет большего простого множителя, чем это.

Пример: 3 ^ 2 x 5 ^ 2 x 13: находятся множители 3, 5 и 13, затем выводится 15, что неверно. Пример: 3 ^ 2 x 5 ^ 2 x 17: находятся множители 3, 5 и 15, затем печатается 17, что оказывается правильным.

person gnasher729    schedule 26.03.2014