-нотация
Символ (читается как О большое) имеет строгое и относительно сложное определение, которое мне бы не хотелось давать тут. Наоборот я хочу показать не сухие формулы и аксиомы, а наглядное представление и понимание, зачем и почему это понятие существует. Итак, перейдем сначала к примерам.
Допустим - это количество операцийпамятиобращений к базе данныхи т.д. в нашем алгоритме. Возьмем несколько функций: , , , и посмотрим на них в небольшом масштабе.
На этом графике мы видим, что и растут намного быстрее и даже имеют сходство в поведении. Но стоит нам чуть-чуть отдалиться...
Теперь ясно видно - лидирует в скорости роста, за ним, и в конце . Сейчас еще сложно понять, чем принципиально отличается отношение к и к . И уменьшая масштаб еще немного...
Вы видите, что уже почти не отличается от вертикальной линии, возрастая очень быстро. Таким образом (вы можете это проверить сами в любой программе построения графиков) всегда обгонит любую линейную функцию и при достаточном масштабе картинка будет аналогична предыдущей будет казаться вертикальной линией, а линейная функция будет выглядеть одинаково на любом графике.
И как раз вот это различие в поведении квадратичных и линейных функций играет большую роль в оценке сложности алгоритмов. Любой алгоритм, который делает операций вместо (даже если достаточно большое, например около ) всегда будет проигрывать линейному. В примере с это разница уже будет заметна при (, ).
Поэтому и вводят обозначение . обозначает все функции, которые имеют такой же или меньший порядок роста как и , например будет содержать , , и даже , так как умножение на константу и прибавление медленных по сравнению с данной функций не меняют картину (вспоминаем последний график, прибавление чего-то маленького к большому не меняет порядок большого, а умножение на константу не может увеличить рост на порядок). Таким же образом содержит все функции вида . Еще интересно заметить, что от быстрых функций содержит более медленные, то есть содержит , но уже не будет лежать в .
Теперь рассмотрим еще несколько важных функций. Мы уже видели поведение и на графиках, и по аналогии можно заметить, что функция при тоже растет быстрее чем все остальные с меньшим . То есть существуют классы функций , каждый из которых содержит все предыдущие, но в тоже время у каждого есть и уникальные функции (например, для класса это многочлены вида , где - произвольные константы, и для того, чтобы многочлен оставался степени ). Например функция принадлежит классу , функция клаccу и т.д. Уже видно, что алгоритм определения этого класса очень прост - мы просто берём главное слагаемое многочлена и отбрасываем константу. В принципе, для других разновидностей функций это правило работает похоже, скоро мы это и увидим.
Следующая важная для нас функция - это логарифм. Можете проверить сами (в википедии даны просто формулы, а доказательства можно найти в любом классическом учебнике мат.анализа), что любая степень логарифма растет медленнее любого многочлена от n, тем самым мы выделяем еще множество новых классов функций (Я пишу без обозначения степени, потому что любые два логарфима с разными степенями можно привести к друг другу обычным домножением на константу, и как вы уже знаете, умножение на константу не меняет порядок роста. Так что запись обозначает логарифмы всех возможных степеней.)
Если понимание уточнений в скобках вызвали у вас трудности, то не обращайте особого внимания, в дальнейшем нам понадобится только то, что растет намного медленнее любой степени или .
И последний класс, который будет необходим для дальнейшего изучения алгоритмов - это экспонента. Тут все аналогично многочленам - тоже есть бесконечное множество классов вида , которые ведут себя точно так же, как и в предыдущих случаях. Опять же легко понять, что экспоненциальные функции всегда обгоняют многочлены и логарифмы, так что будьте осторожными с ними - если ваш алгоритм делает, например, операций (а при это уже около ), то стоит хорошенько подумать, нужен ли такой алгоритм вообще.
Под конец хочется отметить важный паттерн, который встречается даже не часто, а почти в каждом сравнении работы алгоритмов. Это - , он является неким промежуточным звеном между почти идеальным временем работы и нередко легко получаемым . Само домножение на логарифм заметно уменьшает количество операций по сравнению с , но в то же время требует сопоставимых умственных усилий и нетривиальной работы. Во многих дальнеших главах мы будем сначала рассматривать несложные реализации работающие за какую-нибудь степень, например , и затем модифицировать алгоритм так, что одно "пропадает", заменяясь на , в нашем случае получается .
Приведем значения некоторых из этих функций в точках , , , . (Значения в таблице отражают скорее порядок и размер чисел, чем точные данные)
Задачи
1)