Алгоритмы, исполнители и сложность
Анализ работы алгоритма, циклы и ветвления, рекурсия, динамическое программирование, игры и оценка сложности.
Анализ алгоритмов
Чтобы понять алгоритм, выполните его «вручную» на небольшом примере, записывая значения переменных в таблицу. Ищите инварианты: что не меняется на каждой итерации цикла. Для циклов со счётчиком полезно записать, как меняется переменная — арифметическая или геометрическая прогрессия.
Рекурсия
- Рекурсивная функция должна иметь базу (условие остановки) и шаг, приближающий к базе.
- Для быстрых вычислений используйте кэш: from functools import lru_cache.
- Глубина рекурсии по умолчанию ограничена: перед глубокой рекурсией вызывайте sys.setrecursionlimit(…).
ПримерРекурсивная функция
Динамическое программирование
Если число способов получить результат выражается через число способов получить меньшие значения, постройте массив d, где d[i] — ответ для i. Для задач на «количество программ» из исполнителя с командами +1, +2, ×2 стоит идти от меньшего числа к большему и проверять обязательные промежуточные точки умножением результатов: путь от A до B через C = (A→C)·(C→B).
ПримерКоличество программ
Теория игр
Позиция выигрышная, если из неё есть ход в проигрышную; позиция проигрышная, если все ходы ведут в выигрышные. Анализируйте позиции с конца: терминальные позиции (достигнута цель) — проигрышные для того, чей ход. «Выиграть за Х ходов» формализуется введением уровня позиции: Петя выигрывает первым ходом, Ваня — вторым и т. д.
ПримерИгра с прибавлением камней
Сложность
- O(n) — один проход, O(n log n) — сортировка, O(n²) — два вложенных цикла.
- Для n ≈ 10⁶ допустимо O(n) и O(n log n); O(n²) уже не успеет.
- Для простых чисел используйте решето Эратосфена вместо перебора делителей до n.