Великий О: верхня межа
"Великий O" ( ) - на сьогоднішній день найпоширеніший. Коли ви аналізуєте складність алгоритму, більшість часу важливо мати деяку верхню межу щодо того, наскільки швидко час виконання¹ зростає, коли збільшується розмір вводу. В основному ми хочемо знати, що запуск алгоритму не займе «занадто довго». Ми не можемо це виразити у фактичних одиницях часу (секундах), оскільки це залежатиме від точної реалізації (те, як написана програма, наскільки хороший компілятор, наскільки швидкий процесор машини,…). Тож ми оцінюємо, що не залежить від таких деталей, а це - скільки часу потрібно, щоб запустити алгоритм, коли ми подаємо його на більшу суму. І нас головним чином хвилює, коли ми можемо бути впевнені, що програма виконана, тому зазвичай ми хочемо знати, що це займе таку-то таку кількість часу чи менше.O
Сказати, що алгоритм має час виконання для вхідного розміру означає, що існує деяка константа така, що алгоритм виконує щонайбільше кроків, тобто час виконання алгоритму росте максимум настільки ж швидко, як і (до коефіцієнта масштабування). Відзначаючи час виконання алгоритму для розміру вводу , неофіційно означає, що до деякого коефіцієнта масштабування.n K KO(f(n))nKf T ( n ) n O ( n ) T ( n ) ≤ f ( n )Kf(n)fT(n)nO(n)T(n)≤f(n)
Нижня межа
Іноді корисно мати більше інформації, ніж верхня межа. - це зворотне значення : воно виражає, що функція зростає принаймні так само швидко, як і інша. означає, що для деякої постійної , або неофіційно, вгору до деякого коефіцієнта масштабування.ΩOT(n)=Ω(g(n))T(N)≥K′g(n)K′T(n)≥g(n)
Коли час роботи алгоритму можна точно визначити, поєднує в собі і : він виражає, що швидкість зростання функції відома, аж до коефіцієнта масштабування. означає, що для деяких констант і . Неформально кажучи, до деякого коефіцієнта масштабування.ΘOΩT(n)=Θ(h(n))Kh(n)≥T(n)≥K′h(n)KK′T(n)≈h(n)
Подальші міркування
Значення "мало" і використовується набагато рідше в аналізі складності. Маленький сильніший за великий ; де позначає зростання, який не швидше, вказує на те, що ріст суворо повільніше. І навпаки, вказує на строго швидший ріст.oωoOOoω
Я був дещо неофіційним у дискусії вище. У Вікіпедії є формальні визначення та більш математичний підхід.
Майте на увазі, що використання знака рівності в і тому подібне є помилковим. Строго кажучи, - це сукупність функцій змінної , і нам слід записати .T(n)=O(f(n))O(f(n))nT∈O(f)
Приклад: деякі алгоритми сортування
Оскільки це досить сухо, дозвольте навести приклад. Більшість алгоритмів сортування мають квадратичний найгірший час виконання, тобто для введення розміру час роботи алгоритму становить . Наприклад, сортування вибору має час виконання , тому що для вибору го елемента потрібні порівняння, для загальної кількості порівнянь. Насправді кількість порівнянь завжди рівно , яка зростає як . Тож ми можемо бути більш точними щодо часової складності вибору: це .nO(n2)O(n2)kn−kn(n−1)/2n(n−1)/2n2Θ(n2)
Тепер візьміть сортування злиття . Сортування сортування також квадратичне ( ). Це правда, але не дуже точно. Сортування сортування насправді має час роботи в гіршому випадку. Як і сортування вибору, робочий потік сортування об'єднань по суті не залежить від форми вводу, і його час роботи завжди аж до постійного мультиплікативного коефіцієнта, тобто це .O(n2)O(nlg(n))nlg(n)Θ(nlg(n))
Далі розглянемо кікспорт . Кікспорт складніший. Це, звичайно, . Крім того, найгірший випадок кваксорбу - квадратичний: найгірший випадок - . Однак найкращий випадок швидкості сортування (коли вхід вже відсортований) лінійний: найкраще, що ми можемо сказати для нижньої межі до кваксорбу в цілому, є . Я не повторюю доказ тут, але середня складність швидкості (середня сума, взята за всі можливі перестановки введення) - .O(n2)Θ(n2)Ω(n)Θ(nlg(n))
Є загальні результати щодо складності алгоритмів сортування в загальних налаштуваннях. Припустимо, що алгоритм сортування може порівнювати лише два елементи одночасно з результатом «так-ні» (або або ). Тоді очевидно, що час роботи будь-якого алгоритму сортування завжди є (де - кількість елементів для сортування), оскільки алгоритм повинен хоча б раз порівняти кожен елемент, щоб знати, куди він поміститься. Цю нижню межу можна виконати, наприклад, якщо вхід вже відсортований і алгоритм просто порівнює кожен елемент із наступним та підтримує їх у порядку (це порівняння). Менш очевидно, що максимальний час роботи обов'язковоx≤yx>yΩ(n)nn−1Ω(nlg(n)) . Можливо, що алгоритм іноді буде робити менше порівнянь, але має бути деяка константа така, що для будь-якого розміру вводу існує принаймні один вхід, на якому алгоритм робить більше порівняння. Ідея доказу - побудувати дерево рішень алгоритму, тобто дотримуватися рішень, які алгоритм приймає за результатами кожного порівняння. Оскільки кожне порівняння повертає результат "так" чи "ні", дерево рішення є двійковим деревом. Єможливі перестановки вводу, і алгоритм повинен розрізняти всі вони, тому розмір дерева рішень дорівнюєKnKnlg(n)n!n!. Оскільки дерево є двійковим деревом, воно потребує глибини щоб вмістити всі ці вузли. Глибина - це максимальна кількість рішень, які приймає алгоритм, тому запуск алгоритму передбачає принаймні стільки порівнянь: максимальний час виконання - .Θ(lg(n!))=Θ(nlg(n))Ω(nlg(n))
¹ Або інше споживання ресурсів, наприклад, простір пам'яті. У цій відповіді я розглядаю лише час роботи.