Асимптотический анализ
Анализ сравнения затрат времени алгоритмов, выполняемых решение экземпляра некоторой задачи, при больших объемах входных данных, называется асимптотическим. Алгоритм, имеющий меньшую асимптотическую сложность, является наиболее эффективным.
В асимптотическом анализе, сложность алгоритма – это функция, позволяющая определить, как быстро увеличивается время работы алгоритма с увеличением объёма данных.
Основные оценки роста, встречающиеся в асимптотическом анализе:
· Ο (О-большое) – верхняя асимптотическая оценка роста временной функции;
· Ω (Омега) – нижняя асимптотическая оценка роста временной функции;
· Θ (Тета) – нижняя и верхняя асимптотические оценки роста временной функции.
Пусть n – величина объема данных. Тогда рост функции алгоритма f(n) можно ограничить функций g(n) асимптотически:
Обозначение | Описание |
f(n) Î Ο(g(n)) | f ограничена сверху функцией g с точностью до постоянного множителя |
f(n) Î Ω(g(n)) | f ограничена снизу функцией g с точностью до постоянного множителя |
f(n) Î Θ(g(n)) | f ограничена снизу и сверху функцией g |
Например, время уборки помещения линейно зависит от площади этого самого помещения (Θ(S)), т. е. с ростом площади в n раз, время уборки увеличиться также в n раз. Поиск имени в телефонной книге потребует линейного времени Ο(n), если воспользоваться алгоритмом линейного поиска (см. п. 6.1), либо времени, логарифмически зависящего от числа записей (Ο(log2(n))), в случае применения двоичного поиска (см. п. 6.2).
Для нас наибольший интерес представляет Ο-функция. Кроме того, в последующих главах, сложность алгоритмов будет даваться только для верхней асимптотической границы.
Под фразой «сложность алгоритма есть Ο(f(n))» подразумевается, что с увеличением объема входных данных n, время работы алгоритма будет возрастать не быстрее, чем некоторая константа, умноженная на f(n).
Важные правила асимптотического анализа:
1. O(k*f) = O(f) – постоянный множитель k (константа) отбрасывается, поскольку с ростом объема данных, его смысл теряется, например:
O(9,1n) = O(n)
2. O(f*g) = O(f)*O(g) – оценка сложности произведения двух функций равна произведению их сложностей, например:
O(5n*n) = O(5n)*O(n) = O(n)*O(n) = O(n*n) = O(n2)
3. O(f/g)=O(f)/O(g) – оценка сложности частного двух функций равна частному их сложностей, например:
O(5n/n) = O(5n)/O(n) = O(n)/O(n) = O(n/n) = O(1)
4. O(f+g) равна доминанте O(f) и O(g) – оценка сложности суммы функций определяется как оценка сложности доминанты первого и второго слагаемых, например:
O(n5+n10) = O(n10)
Подсчет количества операций – дело утомительное и, что важно, совсем не обязательное. Исходя из выше перечисленных правил, чтобы определить сложность алгоритма, не нужно, как мы это делали прежде, считать все операции, достаточно знать какой сложностью обладает та или иная конструкция алгоритма (оператор или группа операторов). Так, алгоритм, не содержащий циклов и рекурсий, имеет константную сложность O(1). Сложность цикла, выполняющего n итераций, равна O(n). Конструкция их двух вложенных циклов, зависящих от одной и той же переменной n, имеет квадратичную сложность O(n2).
Вот наиболее часто встречающиеся классы сложности:
· O(1) – константная сложность;
· О(n) – линейная сложность;
· О(na) – полиномиальная сложность;
· О(log(n)) – логарифмическая сложность;
· O(n*log(n)) – квазилинейная сложность;
· O(2n) – экспоненциальная сложность;
· O(n!) – факториальная сложность.