← Назад к курсу

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

В этом уроке представлено 30 типовых алгоритмических задач с подробными математическими обоснованиями без кода на Python.

1. Проверка числа на простоту

Математическое обоснование:

  • Число простое, если имеет ровно два делителя: 1 и само себя.
  • Достаточно проверять делители только до √n, так как если n = a×b, то хотя бы один из множителей ≤ √n.
  • Исключаем числа < 2, отрицательные числа и чётные числа больше 2.
  • Оптимизация: проверяем делимость на 2 отдельно, затем на нечётные числа.

2. Разложение числа на простые множители

Математическое обоснование:

  • Основная теорема арифметики: каждое натуральное число > 1 единственным образом раскладывается на простые множители.
  • Алгоритм: последовательно делим число на наименьший простой делитель, пока оно не станет равным 1.
  • Достаточно проверять делители до √n, так как если после проверки всех делителей ≤ √n остаток > 1, то этот остаток — простой множитель.

3. Алгоритм Евклида для НОД

Математическое обоснование:

  • Основан на свойстве: НОД(a, b) = НОД(b, a mod b).
  • Доказательство: если d делит и a, и b, то d делит и их разность a - b, и наоборот.
  • Алгоритм заканчивается, когда остаток становится равным 0, последний ненулевой остаток — НОД.
  • Сложность: O(log min(a, b)).

4. Вычисление НОК через НОД

Математическое обоснование:

  • Связь НОК и НОД: a × b = НОД(a, b) × НОК(a, b).
  • Следует из канонического разложения чисел на простые множители.
  • Поэтому НОК(a, b) = |a × b| / НОД(a, b).
  • Для избежания переполнения лучше вычислять как: a / НОД(a, b) × b.

5. Быстрое возведение в степень

Математическое обоснование:

  • Используем двоичное представление степени: xⁿ можно представить как произведение x^(2^k) для тех k, где соответствующий бит n равен 1.
  • Свойство степени: x^(a+b) = x^a × x^b.
  • Алгоритм: на каждом шаге возводим основание в квадрат, а если текущий бит степени = 1, умножаем результат.
  • Сложность: O(log n) вместо O(n).

6. Решето Эратосфена для поиска простых чисел

Математическое обоснование:

  • Принцип: вычеркиваем все числа, кратные найденным простым.
  • Начинаем с 2, вычеркиваем все кратные 2, затем переходим к следующему невычеркнутому числу.
  • Достаточно проверять до √n, так как любое составное число n имеет делитель ≤ √n.
  • Оптимизация: начинаем вычеркивать с p², так как меньшие кратные уже вычеркнуты.

7. Числа Фибоначчи

Математическое обоснование:

  • Рекуррентное соотношение: F(n) = F(n-1) + F(n-2), F(0)=0, F(1)=1.
  • Золотое сечение: F(n) ≈ φⁿ/√5, где φ = (1+√5)/2.
  • Для быстрого вычисления используем матричное представление или метод удвоения.
  • Свойство: F(n+m) = F(n)F(m+1) + F(n-1)F(m).

8. Проверка числа на совершенность

Математическое обоснование:

  • Совершенное число равно сумме своих собственных делителей.
  • Теорема Евклида-Эйлера: чётное совершенное число имеет вид 2^(p-1)(2^p - 1), где 2^p - 1 — простое (простое Мерсенна).
  • Для проверки: находим все делители до √n, суммируем их с соответствующими парными делителями.

9. Факториал и его вычисление

Математическое обоснование:

  • n! = 1×2×3×...×n (определение).
  • Рекуррентное соотношение: n! = n × (n-1)!, 0! = 1.
  • Формула Стирлинга: n! ≈ √(2πn) × (n/e)^n.
  • Для больших n используем алгоритмы типа Карацубы или преобразование Фурье.

10. Комбинаторные числа (биномиальные коэффициенты)

Математическое обоснование:

  • C(n,k) = n!/(k!(n-k)!) (определение).
  • Треугольник Паскаля: C(n,k) = C(n-1,k-1) + C(n-1,k).
  • Свойство симметрии: C(n,k) = C(n,n-k).
  • Для вычисления без переполнения используем рекуррентное соотношение или формулу с умножением и делением.

11. Проверка числа на палиндром

Математическое обоснование:

  • Число-палиндром читается одинаково слева направо и справа налево.
  • Алгоритм: переворачиваем число и сравниваем с оригиналом.
  • Для переворота: последовательно берем последнюю цифру (n mod 10) и добавляем к перевернутому числу.

12. Перевод между системами счисления

Математическое обоснование:

  • Основано на позиционной записи числа: число в системе с основанием b = Σ d_i × b^i.
  • Из десятичной в другую: последовательное деление на основание с записью остатков.
  • Из другой в десятичную: суммирование произведений цифр на степени основания.
  • Для быстрого перевода между степенями двойки используем группировку бит.

13. Вычисление суммы цифр числа

Математическое обоснование:

  • Используем свойство: последняя цифра числа n равна n mod 10.
  • Сумма цифр: последовательно берем n mod 10, добавляем к сумме, затем n = n // 10.
  • Повторяем до n = 0.
  • Для больших чисел можно использовать свойства делимости на 9.

14. Проверка делимости

Математическое обоснование:

  • Признаки делимости основаны на свойствах сравнений по модулю.
  • Например, для делимости на 3: сумма цифр делится на 3.
  • Для делимости на 4: последние две цифры делятся на 4.
  • Общий метод: вычисление остатка от деления.

15. Поиск всех делителей числа

Математическое обоснование:

  • Делители идут парами: если d делит n, то n/d тоже делитель.
  • Достаточно искать делители до √n, затем добавлять парные.
  • Для нахождения всех делителей: факторизуем число и генерируем все комбинации степеней простых множителей.

16. Функция Эйлера (количество взаимно простых чисел)

Математическое обоснование:

  • φ(n) = количество чисел ≤ n, взаимно простых с n.
  • Мультипликативность: если НОД(a,b)=1, то φ(ab) = φ(a)φ(b).
  • Формула: φ(n) = n × Π(1 - 1/p), где произведение по всем простым p, делящим n.
  • Для простого p: φ(p) = p-1.

17. Расширенный алгоритм Евклида

Математическое обоснование:

  • Находит такие x,y, что ax + by = НОД(a,b) (соотношение Безу).
  • Использует ту же итерацию, что и обычный алгоритм Евклида, но сохраняет коэффициенты.
  • Коэффициенты обновляются по формулам: x_new = y_old, y_new = x_old - ⌊a/b⌋×y_old.

18. Решение линейных диофантовых уравнений

Математическое обоснование:

  • Уравнение ax + by = c имеет решение тогда и только тогда, когда НОД(a,b) делит c.
  • Находим частное решение с помощью расширенного алгоритма Евклида.
  • Общее решение: x = x₀ + (b/d)t, y = y₀ - (a/d)t, где d = НОД(a,b).

19. Проверка числа на степень двойки

Математическое обоснование:

  • Число является степенью двойки, если в двоичной записи ровно одна единица.
  • Математически: n > 0 и (n & (n-1)) = 0.
  • Доказательство: если n = 2^k, то n-1 имеет все единицы в младших k битах.

20. Быстрое умножение по модулю (modular exponentiation)

Математическое обоснование:

  • Использует тот же принцип, что и быстрое возведение в степень, но с учётом модуля.
  • Свойство: (a × b) mod m = [(a mod m) × (b mod m)] mod m.
  • Позволяет работать с очень большими степенями без переполнения.

21. Генерация всех перестановок

Математическое обоснование:

  • Количество перестановок n элементов: n!.
  • Алгоритм Хипа: генерирует перестановки с помощью минимального числа транспозиций.
  • Рекурсивный метод: на каждом шаге фиксируем один элемент и генерируем перестановки оставшихся.

22. Генерация всех подмножеств

Математическое обоснование:

  • Количество подмножеств n-элементного множества: 2^n.
  • Битовое представление: каждое подмножество соответствует битовой маске длины n.
  • Генерация всех масок от 0 до 2^n - 1 даёт все подмножества.

23. Задача о рюкзаке (0-1)

Математическое обоснование:

  • Динамическое программирование: dp[i][w] = максимальная стоимость при использовании первых i предметов и весе w.
  • Рекуррентное соотношение: dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight_i] + value_i).
  • Сложность: O(n×W), где n — количество предметов, W — максимальный вес.

24. Алгоритм Кадана (максимальная сумма подмассива)

Математическое обоснование:

  • Основан на идее динамического программирования.
  • Пусть dp[i] — максимальная сумма подмассива, заканчивающегося в позиции i.
  • Рекуррентное соотношение: dp[i] = max(arr[i], dp[i-1] + arr[i]).
  • Оптимизация по памяти: храним только предыдущее значение.

25. Задача о размене монет

Математическое обоснование:

  • Динамическое программирование: dp[x] = минимальное количество монет для суммы x.
  • Рекуррентное соотношение: dp[x] = min(dp[x-coin] + 1) по всем номиналам coin.
  • Начинаем с dp[0] = 0, dp[>0] = ∞.

26. Поиск наибольшей возрастающей подпоследовательности (LIS)

Математическое обоснование:

  • Динамическое программирование: dp[i] = длина LIS, заканчивающейся в i.
  • Рекуррентное соотношение: dp[i] = 1 + max(dp[j]) для всех j < i, где arr[j] < arr[i].
  • Алгоритм с бинарным поиском: O(n log n) с помощью поддержания массива возможных хвостов.

27. Расстояние Левенштейна (редакционное расстояние)

Математическое обоснование:

  • Динамическое программирование: dp[i][j] = расстояние между префиксами длины i и j.
  • Рекуррентное соотношение: dp[i][j] = min(
    dp[i-1][j] + 1 (удаление),
    dp[i][j-1] + 1 (вставка),
    dp[i-1][j-1] + (0 если символы равны, иначе 1) (замена)
    )

28. Задача о рюкзаке с бесконечными предметами

Математическое обоснование:

  • Динамическое программирование: dp[w] = максимальная стоимость для веса w.
  • Рекуррентное соотношение: dp[w] = max(dp[w], dp[w-weight_i] + value_i) для всех предметов i.
  • Порядок обхода: от 0 до W, позволяя многократное использование предметов.

29. Проверка графа на двудольность

Математическое обоснование:

  • Граф двудольный, если его вершины можно раскрасить в два цвета так, что смежные вершины имеют разные цвета.
  • Теорема: граф двудольный тогда и только тогда, когда не содержит циклов нечётной длины.
  • Алгоритм: поиск в ширину или глубину с раскраской.

30. Алгоритм Дейкстры для кратчайших путей

Математическое обоснование:

  • Жадный алгоритм: на каждом шаге выбираем вершину с минимальным текущим расстоянием.
  • Принцип оптимальности Беллмана: если кратчайший путь из A в C проходит через B, то его часть от A до B тоже кратчайшая.
  • Корректность обеспечивается неотрицательностью весов рёбер.

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