Flat Preloader Icon

Project Euler (answers)

Язык:

Язык:

Проект Эйлера: Ответ и решение 3 задачи (Наибольший простой делитель)

Проект Эйлера 3 задача. Никаких чудес! Просто (прямо из штаб-квартиры Google) добываем секретный алгоритм и ответ получаем мгновенно.

Узнать больше в телеграм-канале: ProjectEuler++

Условия задачи

Простые делители числа 13195 – это 5, 7, 13 и 29.

Какой самый большой делитель числа 600851475143, являющийся простым числом?

Проект Эйлера 3 задача.
Эти задачи выносят мозг)

Описание работы программы - проект Эйлера (3 задача)

проект Эйлера (3 задача) - решение
Программа для решения задачи Эйлера #3

Бесперспективность наивного алгоритма

Поиск ответа обычным перебором

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

  • Число 600851475143 последовательно делится на ряд чисел 2, 3, 4 600851475142.
  • Каждый раз, когда 600851475143 делится нацело (т.е. остаток от целочисленного деления равен нулю), необходимо проверить простое ли число делитель или нет.
  • Чтобы найденный в итоге делитель однозначно был  самым большим, понадобится перебрать все делители вплоть до 600851475143.

Однако это уже 600 миллиардов операций!

Наивный алгоритм определения простого числа

При этом основной проблемой в задаче будет проверка, простые ли числа. Потому что, если следовать самому определению, число простое в том случае, если делится только на единицу и на само себя.

Чтобы в итоге проверить, простое ли число 5, понадобятся следующие операции:

  • делим 5 на 2 и получаем остаток от деления 1;
  • 5 делим на 3 (остаток2);
  • и в конце 5 делим на 4 (остаток1).

Убедившись, что ни одно число не делит 5 нацело, можно сделать вывод, что оно простое.

Конечно, в программе эти операции будут выглядеть в виде одной строчки кода. Однако таким способом для проверки числа 600851475143 понадобится 600 миллиардов операций деления. А для числа перед ним (600851475142) еще 600 миллиардов операций.

Мы видим в итоге, что работа простого алгоритма потребует миллиарды миллиардов операций.

Описание алгоритма работы программы - проект Эйлера (3 задача)

Раскладываем проверяемое число на два множителя

Для простоты объяснения идеи алгоритма программы возьмем не 600851475143, а меньшее число1000.

Число обратное простому называется составным и его можно разложить на множители. 

Например 1000 = 2 * 500, и потому 1000 делится как на 2, так и на 500. При этом 2 – минимальный делитель числа, а 500максимальный.

Получается, максимальный делитель числа 1000 найден (500)? Однако простое ли это число (500)? 

Для ответа нужно повторить цикл и попытаться разложить уже число 500 на множители. И делать так до тех пор, пока последний максимальный делитель в итоге перестанет раскладываться на множители.

Алгоритм получаем следующий:

  • 1000 % 2 – получаем остаток от деления равный нулю, потому что 1000 делится нацело на 2
  • Далее вместо того, чтобы продолжать делить 1000 на 3 4 5 999, вычисляем максимальный делитель.
  • 1000 / 2 = 500 – максимальный делитель числа 1000
  • 500 / 2 = 250
  • 250 /2 = 125
  • 125 / 5 = 25
  • 25 / 5 = 5максимальный простой делитель числа 1000.

Как мы видим в итоге тысяча тысяч операций по поиску максимального простого делителя числа 1000 сократилось до 5 итераций.

"Продвинутый" алгоритм определения простого числа

Именно в этой программе этот “лайфхак” не нужен, потому что алгоритм (выше) уже достаточно оптимизирует код. Однако задачи проекта Эйлер выстроены по мере нарастания сложности и цепляются друг за друга. Знания и навыки, выработанные в первых задачах, пригодятся в последующих. Потому можете считать это бонусом)

Возьмем небольшое число (12) и будем раскладывать его на множители:

  • 2 * 6
  • 3 * 4
  • 4 * 3
  • 6 * 2

Ясно видно, что первое и второе множества повторяют третье и четвертое. Только множители меняются местами.

Потому, чтобы определить простое ли число (12), можно не делить его на весь ряд чисел: 2, 3, 4 10, 11. Достаточно проверить до корня квадратного из числа (sqrt(12) = 3.464). В данном случае это числа 2, 3, 4.

Для пятнадцатизначных чисел, например,  ускорение работы в итоге будет кратное

* Примечания:

Подключаем стандартную математическую библиотеку языка Си. Необходима для работы функции sqrt()

  • Функция sqrt(answer) возвращает квадратный корень из answer (внимание!) в вещественном виде. 
  • int sq_answ = sqrt(answer) – присваиваем вычисленное значение переменной sq_answ (внимание!) в целочисленном виде.
  • Т.е. от числа вида 12.34567 отбрасывается дробная часть – 12
  • Это необходимо т.к. while() работает с целочисленными значениями
  • (answer % div) – любое ненулевое число внутри оператора if() будет восприниматься как true (истина).
  • Условие будет срабатывать и будет работать код после if()
  • ! – операция “не” инвертирует значение true в false и наоборот.
  • Таким образом !(answer % div) – сработает если остаток от деления будет равен нулю (answer % div == 0), следовательно числа делятся нацело
 
То же самое, что answer = answer / div
  • Выводим значение переменной answer на экран
  • %lld – с помощью суффиксов “ll” указываем, что тип числа long long int

Измеряем время работы программы

Для измерения времени работы программы подключаем библиотеку time.h:

  • #include <time.h> 

С помощью функции clock() измеряем время начала (begin) и окончания (end) работы программы. Время работы в итоге будем находить как разность между ними.

* Примечания:

clock_t это тип данных, определенный через typedef. По сути это обычный целочисленный long

(double) приводит число (в данном случае – результат вычислений end – begin) к вещественному типу данных double (числа с точкой)

CLOCKS_PER_SEC – это константа, сохраненная в библиотеке и зависящая от типа устройства, на котором запускается программа. В данном случае – это просто 1000

Проект Эйлера (3 задача) - ответ и скорость работы программы

проект Эйлера (3 задача) - ответ
3 задача ответ

В итоге ответ на 3 задачу проекта Эйлера составил 6857, программа работает быстро (ноль миллисекунд). 

Однако для сравнения я пробовал работу наивного алгоритма. Даже после уменьшения проверяемого числа в сто раз, программа считала почти минуту. 

P.S. Выражаю глубочайшую признательность Сергею Балакиреву за его титанический труд по созданию бесплатных курсов и в частности, за “Язык программирования C/C++ для начинающих“.

проект Эйлера
Курс "C/C++ для начинающих"

Есть вопросы, господа? Отвечаем спокойно, четко и только по делу))

Есть вопросы, господа?

Проект Эйлера (ответы) | © 2023-2024 | eulerproject.ru

Проект Эйлера (ответы) | © 2023-2024 | eulerproject.ru

© 2023-2024 | eulerproject.ru