1. Теория чисел: алгоритм Евклида, решето Эратосфена и модульная арифметика
Теория чисел: алгоритм Евклида, решето Эратосфена и модульная арифметика
Добро пожаловать в курс «Математика для олимпиадного программирования». Если вы занимаетесь спортивным программированием, но чувствуете неуверенность, когда дело доходит до математических задач — вы в правильном месте. Мы не будем углубляться в абстрактные доказательства, которые нужны только профессорам. Наша цель — понять фундамент, на котором строятся эффективные алгоритмы.
Первая статья посвящена «трем китам» базовой теории чисел, которые встречаются в каждой второй задаче уровня Div2/Div3 на соревнованиях: модульной арифметике, поиску НОД (наибольшего общего делителя) и простым числам.
Модульная арифметика: математика на циферблате
В задачах часто просят вывести ответ «по модулю ». Зачем? Потому что ответы могут быть настолько огромными, что не поместятся даже в 64-битный тип данных (long long). Модульная арифметика позволяет нам работать с огромными числами, сохраняя их в пределах разумного диапазона.
Представьте себе обычные часы. Если сейчас 10 часов, то через 5 часов будет не 15, а 3 часа. Это и есть арифметика по модулю 12.
!Аналогия модульной арифметики с часами: переход через границу возвращает к началу
Основная операция
Операция взятия остатка обозначается символом % в коде или в математике.
Где:
Например, , так как .
Свойства сложения и умножения
Самое важное правило: мы можем брать остаток на любом этапе вычислений. Это спасает от переполнения типов данных.
Для сложения:
Где:
Для умножения:
Где:
Это значит, что если вам нужно найти , вы можете сначала умножить на , взять остаток, затем умножить на и снова взять остаток.
Коварное вычитание
С вычитанием в программировании есть нюанс. В математике остаток всегда неотрицателен (). Однако в C++ или Java оператор % для отрицательных чисел может вернуть отрицательный результат (например, может вернуть ).
Чтобы избежать ошибок, используйте формулу:
Где:
Алгоритм Евклида: поиск НОД
Наибольший общий делитель (НОД, или GCD — Greatest Common Divisor) двух чисел и — это самое большое число, на которое делятся и , и без остатка.
Наивный способ — перебирать все числа от до 1 — работает слишком медленно (). Если числа порядка , программа не уложится в лимит времени.
Древнегреческий математик Евклид придумал гениальный и быстрый способ. Идея основана на факте:
> Если мы заменим большее число на остаток от деления большего на меньшее, НОД не изменится.
Формула алгоритма
Где:
!Геометрическая интерпретация алгоритма Евклида: последовательное уменьшение прямоугольника
Пример работы
Найдем НОД(30, 12):
Этот алгоритм работает очень быстро — за логарифмическое время . Даже для чисел с сотней знаков он найдет ответ мгновенно.
НОК (Наименьшее общее кратное)
Зная НОД, легко найти НОК (LCM — Least Common Multiple) — наименьшее число, которое делится и на , и на :
Где:
(a / GCD(a, b)) * b, чтобы избежать переполнения при умножении).Решето Эратосфена: массовый поиск простых чисел
Простое число — это число, которое имеет ровно два делителя: 1 и само себя (2, 3, 5, 7, 11...).
Частая задача: найти все простые числа от 1 до . Если проверять каждое число отдельно, это будет долго. Эратосфен придумал алгоритм «вычеркивания».
Алгоритм
!Визуализация процесса вычеркивания чисел в решете Эратосфена
Оптимизация
Начинать вычеркивать кратные числа для числа можно не с , а сразу с . Почему? Потому что меньшие кратные (например, , ) уже были вычеркнуты, когда мы обрабатывали числа 2, 3 и т.д.
Сложность
Сложность этого алгоритма удивительно низкая:
Где:
Фактически, алгоритм работает почти линейно. Для (10 миллионов) решето Эратосфена отрабатывает меньше чем за секунду на современном компьютере.
Итоги
Сегодня мы заложили фундамент:
Эти алгоритмы — ваш «швейцарский нож». В следующих статьях мы научимся применять их для решения более сложных комбинаторных задач.