Все алгоритмы можно оценить по нескольким параметрам. Это требуется для того, чтобы грамотно распределить ресурсы, выбрать оптимальные методы решения задач и грамотно расходовать время и память компьютера.
В зависимости от ограниченности ресурсов можно пожертвовать либо одним, либо другим. В идеале стремятся к максимальной экономии. Однако есть отдельные виды алгоритмов, которые не позволяют сократить, к примеру, объем используемой памяти. Как правило, это рекурсивные алгоритмы, когда в память закладывается большое количество итераций, результат которых пересчитывается при обратном проходе.
Всего же имеется восемь основных параметров, по которым можно комплексно провести анализ алгоритма:
Она определяет, как увеличивается время выполнения алгоритма в зависимости от объема входных данных. Временная сложность выражается с помощью асимптотической нотации (например, O(n)), О(n^2), O(log n)) и характеризует:
Наихудший случай (O-нотация): максимальное количество операций, выполняемых алгоритмом.
Средний случай: среднее количество операций для случайного входа.
Лучший случай (Omega-нотация): минимальное количество операций, выполняемых алгоритмом.
Пример:
Сортировка пузырьком в худшем случае имеет временную сложность O(n^2), так как она выполняет n⋅(n−1)/2 сравнений.
Она показывает, сколько памяти требуется алгоритму для выполнения задач в зависимости от объема входных данных. Пространственная сложность включает:
Постоянные затраты (стек вызовов, переменные).
Динамические затраты (временные структуры, создаваемые во время работы алгоритма).
Пример:
Рекурсивный алгоритм может потреблять больше памяти из-за использования стека вызовов, в отличие от итеративного.
Анализируется рост функции сложности при больших значениях входных данных. Основные категории роста:
Линейный (O(n)) – растет пропорционально.
Логарифмический (O(log n)) – рост замедляется с увеличением данных.
Квадратичный (O(n^2)) – быстрое увеличение числа операций.
Экспоненциальный (О(2^n)) – крайне неэффективный рост.
Количество базовых операций, таких как:
Сравнения, присваивания, арифметические действия.
Операции ввода/вывода (обычно исключаются из анализа).
Пример:
В цикле из n итераций for (i = 0; i < n; i++), выполняющем одну операцию, будет n операций.
Сложность может зависеть от используемых структур данных. Например, вставка в:
Хэш-таблицу (O(1) в среднем случае).
Сбалансированное дерево (O(log n)).
Если алгоритм состоит из нескольких частей, необходимо учитывать сложность каждой из них:
Сложность циклов. Вложенные циклы увеличивают сложность (например, вложенный цикл на n дает O(n^2)).
Рекурсия. Вычисляется по числу вызовов и размерам задачи (анализ с использованием рекуррентных уравнений).
Анализируется, как алгоритм обрабатывает:
Маленькие объемы данных.
Крайние случаи (пустой вход, большие данные, повторяющиеся элементы).
Теоретическая сложность должна быть сопоставлена с практическими результатами:
Влияние констант (реальный O(n) алгоритм с большой константой может быть медленнее O(n2) для небольших n).
Эффективность в контексте конкретной архитектуры (кэш-память, параллельные вычисления).
Цель анализа алгоритма может быть достигнута при просчитывании одного, наиболее важного, параметра. Или же быть комплексным. В этом случае используется принцип распределения по значимости.
Чтобы проверить основы анализа алгоритмов, рассмотрим алгоритмы сортировки.
Асимптотическое поведение:
Лучший случай: O(n)) (если массив уже отсортирован).
Средний случай: O(n^2).
Худший случай: O(n^2).
Число операций:
Сравнений: n(n−1)/2≈O(n^2) в худшем случае.
Перестановок: до n(n−1)/2≈O(n^2) в худшем случае.
Временная сложность:
Алгоритм проходит через массив n раз, выполняя сравнение и, при необходимости, перестановку для каждой пары соседних элементов.
Пример:
Для массива длиной 5 в худшем случае потребуется 10 сравнений и 10 перестановок.
Асимптотическое поведение:
Лучший случай: O(n) (если массив уже отсортирован).
Средний случай: O(n^2).
Худший случай: O(n^2).
Число операций:
Сравнений: O(n^2) в худшем случае (каждый элемент сравнивается с предыдущими).
Перестановок: O(n^2) в худшем случае.
Временная сложность:
Алгоритм вставляет элементы один за другим в отсортированную часть массива. Для каждого элемента требуется найти подходящее место в n−1 шагах в худшем случае.
Пример:
Для массива длиной 5 в худшем случае потребуется 10 сравнений и 10 перестановок.
Асимптотическое поведение:
Лучший случай: O(n log n) (при удачном выборе опорного элемента).
Средний случай: O(n log n).
Худший случай: O(n^2) (если массив разбивается на неравные части).
Число операций:
Разбиение массива: до n−1 сравнений на каждом уровне.
Уровней рекурсии: log n в среднем случае, n в худшем.
Временная сложность:
Каждое разбиение массива требует n операций, а общее количество уровней — log n при сбалансированных разбиениях.
Пример:
Для массива длиной 8 потребуется около 24 операций (в среднем случае).
Асимптотическое поведение:
Лучший случай: O(n log n).
Средний случай: O(n log n).
Худший случай: O(n log n).
Число операций:
Разбиение массива: log n уровней.
Слияние: n операций на каждом уровне.
Временная сложность:
Алгоритм делит массив пополам до единичных элементов, затем объединяет их, сохраняя порядок, за O(n log n).
Пример:
Для массива длиной 8 потребуется около 24 операций.
Асимптотическое поведение:
Лучший случай: O(n log n).
Средний случай: O(n log n).
Худший случай: O(n log n).
Число операций:
Построение кучи: O(n).
Удаление элементов из кучи: O(log n) операций для каждого элемента.
Временная сложность:
Алгоритм сначала строит двоичную кучу за O(n), затем извлекает максимальный элемент n раз за O(log n) операций.
Пример:
Для массива длиной 8 потребуется около 15 операций на построение кучи и 24 операции на извлечение элементов.
Использование такого анализа алгоритмов необходимо для приблизительного понимания ресурсов, которые будут затрачены на его исполнение.
При подготовке к ЕГЭ по данной теме можно встретить типовые вопросы:
1. Что показывает временная сложность алгоритма?
2. Какая временная сложность соответствует лучшему случаю для пузырьковой сортировки?
3. Какую сложность имеет сортировка слиянием в худшем случае?
4. Какая характеристика отличает рекурсивные алгоритмы от итеративных?
5. Какая категория роста асимптотического поведения алгоритма считается крайне неэффективной?