Що таке асимптотичні функції? Що таке асимптота?
Враховуючи функцію f (n), яка описує кількість ресурсів (час процесора, оперативна пам'ять, дисковий простір тощо), що споживається алгоритмом при застосуванні до вводу розміром n , ми визначаємо до трьох асимптотичних позначень для опису його продуктивності для великий н .
Асимптота (або асимптотична функція) - це просто якась інша функція (або відношення) g (n), до якої f (n) стає все ближче, коли n росте більше і більше, але ніколи не досягає цього. Перевага розмови про асимптотичні функції полягає в тому, що вони, як правило, набагато простіше говорити, навіть якщо вираз для f (n) надзвичайно складний. Асимптотичні функції використовуються як частина обмежувальних позначень, що обмежують f (n) вище чи нижче.
(Примітка: у сенсі, застосованому тут, асимптотичні функції близькі лише до вихідної функції після виправлення деякого постійного ненульового коефіцієнта, оскільки всі три нотації великого O / Θ / Ω не враховують цей постійний чинник від їх врахування.)
Що таке три асимптотичні обмежувальні позначення та чим вони відрізняються?
Усі три позначення використовуються так:
f (n) = O (g (n))
де f (n) - функція, що цікавить, а g (n) - якась інша асимптотична функція, з якою ви намагаєтесь наблизити f (n) . Це не слід сприймати як рівність у жорсткому розумінні, а формальне твердження між тим, як швидко f (n) зростає відносно n порівняно з g (n) , оскільки n стає великим. Пуристи часто використовують альтернативне позначення f (n) ∈ O (g (n)), щоб підкреслити, що символ O (g (n)) - це справді ціле сімейство функцій, які мають загальний темп зростання.
Позначення Big-ϴ (Theta) визначають рівність зростання f (n) до постійного коефіцієнта (докладніше про це пізніше). Він поводиться аналогічно =
оператору за темпами зростання.
Позначення Big-O описує верхнє обмеження на ріст f (n) . Він поводиться аналогічно ≤
оператору за темпами зростання.
Позначення Big-Ω (Omega) описують нижчу границю на зростанні f (n) . Він поводиться аналогічно ≥
оператору за темпами зростання.
Існує багато інших асимптотичних позначень , але вони зустрічаються майже не так часто в літературі з інформатики.
Нотації Big-O та його заклики часто є способом порівняння складності часу .
Яка складність у часі?
Часова складність є фантазійним терміном за кількість часу T (n), яке потрібно алгоритму для виконання як функції його вхідного розміру n . Це можна виміряти у кількості реального часу (наприклад, секунд), кількості інструкцій процесора тощо. Зазвичай передбачається, що алгоритм буде працювати на вашому щоденному комп'ютері архітектури фон Неймана . Але звичайно, ви можете використовувати складний час, щоб поговорити про більш екзотичні обчислювальні системи, де все може бути інакше!
Також звичайно говорити про складність простору, використовуючи позначення Big-O. Космічна складність - це обсяг пам'яті (сховища), необхідний для завершення алгоритму, яким може бути оперативна пам'ять, диск тощо.
Можливо, один алгоритм повільніше, але використовує менше пам'яті, а інший - швидший, але використовує більше пам'яті. Кожен може бути більш доцільним при різних обставинах, якщо ресурси обмежені по-різному. Наприклад, вбудований процесор може мати обмежену пам'ять і надавати перевагу повільнішому алгоритму, тоді як сервер у центрі обробки даних може мати великий об'єм пам'яті та сприяти більш швидкому алгоритму.
Обчислення Big-ϴ
Обчислення алгоритму Big-ϴ - це тема, яка може заповнити невеликий підручник або приблизно півсеместру бакалаврського класу: цей розділ буде висвітлювати основи.
Дана функція f (n) у псевдокоді:
int f(n) {
int x = 0;
for (int i = 1 to n) {
for (int j = 1 to n) {
++x;
}
}
return x;
}
Яка складність у часі?
Зовнішня петля проходить n разів. Кожен раз, коли працює зовнішня петля, внутрішня петля працює n разів. Це ставить час роботи на T (n) = n 2 .
Розглянемо другу функцію:
int g(n) {
int x = 0;
for (int k = 1 to 2) {
for (int i = 1 to n) {
for (int j = 1 to n) {
++x;
}
}
}
return x;
}
Зовнішня петля проходить двічі. Середня петля проходить n разів. Кожен раз, коли проходить середня петля, внутрішня петля працює n разів. Це ставить час роботи на T (n) = 2n 2 .
Тепер питання полягає в тому, який асимптотичний час роботи обох функцій?
Щоб обчислити це, ми виконуємо два етапи:
- Видаліть константи. Коли алгоритми збільшуються в часі за рахунок вхідних даних, інші умови домінують у процесі виконання, роблячи їх невагомими.
- Видаліть усі, крім найбільшого терміну. Коли n переходить до нескінченності, n 2 швидко випереджає n .
Тут головне - зосередити увагу на домінуючих термінах та спростити їх .
T (n) = n 2 ∈ ϴ (n 2 )
T (n) = 2n 2 ∈ ϴ (n 2 )
Якщо у нас є інший алгоритм з декількома термінами, ми б спростили його, використовуючи ті самі правила:
T (n) = 2n 2 + 4n + 7 ∈ ϴ (n 2 )
Ключовим з усіх цих алгоритмів є те, що ми орієнтуємося на найбільші терміни та видаляємо константи . Ми дивимось не на фактичний час роботи, а на відносну складність .
Обчислення Big-Ω та Big-O
По-перше, слід попередити, що в неофіційній літературі "Big-O" часто трактується як синонім Big-Θ, можливо, тому, що грецькі літери є складними для введення. Тож якщо хтось із синіх тонів запитує у вас алгоритму Big-O, він, ймовірно, хоче його Big-Θ.
Тепер, якщо ви дійсно хочете обчислити Big-Ω та Big-O у формальних сенсах, визначених раніше, у вас є основна проблема: існує безліч безлічі описів Big-Ω та Big-O для будь-якої заданої функції! Це як запитати, якими є числа, менші або рівні 42. Є багато можливостей.
Для алгоритму з T (n) ∈ ϴ (n 2 ) будь-яке з перелічених нижче є формально допустимими твердженнями:
- T (n) ∈ O (n 2 )
- T (n) ∈ O (n 3 )
- T (n) ∈ O (n 5 )
- T (n) ∈ O (n 12345 × e n )
- T (n) ∈ Ω (n 2 )
- T (n) ∈ Ω (n)
- T (n) ∈ Ω (log (n))
- T (n) ∈ Ω (log (log (n)))
- T (n) ∈ Ω (1)
Але неправильно твердити T (n) ∈ O (n) або T (n) ∈ Ω (n 3 ) .
Що таке відносна складність? Які класи алгоритмів існують?
Якщо ми порівняємо два різних алгоритми, то їх складність із входом до нескінченності зазвичай зростатиме. Якщо ми подивимось на різні типи алгоритмів, вони можуть залишитися відносно однаковими (скажімо, відрізнятися постійним фактором) або можуть сильно розходитися. Це причина для аналізу Big-O: щоб визначити, чи буде алгоритм працювати з великими входами.
Класи алгоритмів розбиваються так:
Θ (1) - константа. Наприклад, вибір першого номера у списку завжди займе стільки ж часу.
Θ (n) - лінійний. Наприклад, повторення списку завжди займе час, пропорційний розміру списку, n .
Θ (log (n)) - логарифмічний (основа зазвичай не має значення). Алгоритми, що розділяють вхідний простір на кожному кроці, наприклад, двійковий пошук, є прикладами.
Θ (n × log (n)) - лінійні часові логарифмічні («лінійні). Ці алгоритми, як правило, ділять і підкоряють ( log (n) ), зберігаючи ( n ) весь вхід. Багато популярних алгоритмів сортування (сортування злиття, Timsort) потрапляють до цієї категорії.
Θ (n m ) - многочлен ( n піднятий до будь-якої постійної m ). Це дуже поширений клас складності, часто зустрічається в вкладених петлях.
Θ (m n ) - експоненціальна (будь-яка константа m, піднята до n ). Багато рекурсивних та графічних алгоритмів підпадають під цю категорію.
Θ (п!) - факторіал. Певні графічні та комбінаторні алгоритми є факторною складністю.
Чи має це щось спільне з найкращим / середнім / гіршим випадком?
Ні. Big-O та його сімейство позначень говорять про конкретну математичну функцію . Вони є математичними інструментами, які допомагають охарактеризувати ефективність алгоритмів, але поняття найкращого / середнього / гіршого випадку не пов'язане з теорією темпів зростання, описаною тут.
Щоб говорити про алгоритм Big-O, потрібно взяти на себе конкретну математичну модель алгоритму з точно одним параметром n
, який повинен описувати "розмір" вхідного сигналу в будь-якому сенсі, який є корисним. Але в реальному світі матеріали мають набагато більшу структуру, ніж просто їх тривалість. Якби це був алгоритм сортування, я міг би харчуватися в рядках "abcdef"
, "fedcba"
або "dbafce"
. Всі вони мають довжину 6, але одна з них вже сортована, одна перевернута, а остання - лише випадкова перемичка. Деякі алгоритми сортування (наприклад, Timsort) працюють краще, якщо вхід вже відсортований. Але як можна включити цю неоднорідність у математичну модель?
Типовий підхід полягає у тому, щоб просто припустити, що вхід походить від якогось випадкового, ймовірнісного розподілу. Потім ви оцінюєте складність алгоритму за всіма входами з довжиною n
. Це дає модель середнього випадку складності алгоритму. Звідси ви можете використовувати нотації Big-O / Θ / Ω як зазвичай для опису середньої поведінки у випадку.
Але якщо ви стурбовані атаками відмови у наданні послуг, можливо, вам доведеться бути песимістичнішими. У цьому випадку безпечніше припустити, що єдиними входами є ті, які викликають найбільше горя за вашим алгоритмом. Це дає вам найгірший варіант складності алгоритму. Згодом ви можете поговорити про Big-O / Θ / Ω тощо з найгіршої моделі.
Аналогічно, ви також можете зосередити свою зацікавленість виключно на входах, з якими ваш алгоритм має найменшу кількість проблем, щоб знайти найкращу модель, а потім подивитись на Big-O / Θ / Ω тощо.