В математиката и информатиката записът Big O (наричан още Голямо O) описва как се увеличава една функция при нарастване на аргумента ѝ. На практика това служи за сравняване на темповете на растеж на различни функции и за оценка на ефективността на алгоритми — колко памет и колко време са необходими за изпълнение спрямо размера на входа.

Исторически, нотацията идва от работите в теория на числата: Паул Бахман (Paul Bachmann, 1837–1920) използва подобни означения в по-ранни издания, а Едмунд Ландау (Edmund Landau, 1877–1938) популяризира тази форма на изразяване. Поради това често се среща и терминът "символи на Ландау".

Какво точно означава Big O?

Интуитивно, Big O дава горна граница за растежа на функцията — казва ни какъв е най-лошият (най-бавният) алгоритъм за дадена величина на входа. Това позволява да се сравняват алгоритми независимо от конкретния хардуер или от малки оптимизации на имплементацията: един алгоритъм с O(1) винаги ще бъде по-ефективен при големи входни размери от алгоритъм с O(n!), дори ако на един конкретен компютър времената могат да варират.

Формално определение

За две положителни функции f ( x ) {\displaystyle f(x)}f(x) и g ( x ) {\displaystyle g(x)}{\displaystyle g(x)}, казваме, че f {\displaystyle f}f е в голямата О на g {\displaystyle g}g (написано f O ( g ) {\displaystyle f\in O(g)}{\displaystyle f\in O(g)} ), ако за достатъчно голямо x {\displaystyle x}x , f ( x ) ≤ k g ( x ) {\displaystyle f(x)\leq k\cdot g(x)}{\displaystyle f(x)\leq k\cdot g(x)} за някаква константа k {\displaystyle k}k .

На по-прост език: f(x) е O(g(x)), ако за всички достатъчно големи x стойността на f(x) не надвишава някаква константа, умножена по g(x). Това означава, че g(x) отговаря за "темпа на растеж" на f(x) (до постоянен коефициент).

Често срещани класове сложност

  • O(1) — константно време: изпълнението не зависи от размера на входа (например достъп до елемент в масив по индекс).
  • O(log n) — логаритмично време: пример е двоично търсене.
  • O(n) — линейно време: една обиколка през всички елементи на масив.
  • O(n log n) — например бързи сортиращи алгоритми като quicksort и mergesort (в среден/добрия случай).
  • O(n^2) — квадратично време: вложени двойни цикли (например прост сортиращ алгоритъм като bubble sort).
  • O(2^n) — експоненциално време: обикновено при проблеми с избиране на подмножества, като решаване на задачи чрез брутално изброяване.
  • O(n!) — факториално време: пример е генериране на всички пермутации на n елемента.

Примери в код (интуиция)

  • Едно присвояване или условна проверка: O(1).
  • Цикъл, който преминава веднъж през n елемента: O(n).
  • Два вложени цикъла, всеки от n итерации: O(n^2).
  • Двоично търсене в сортиран масив: O(log n).

Най-лош, среден и най-добър случай; пространство

Big O обикновено описва най-лошия случай (worst-case). Има и други нотации: Θ (Theta) описва точна асимптотична граница (горна и долна), а Ω (Omega) — долна граница (best-case). Освен времева сложност, говорим и за пространствена сложност (колко допълнителна памет използва алгоритъмът), която се анализира по същия начин с Big O.

Защо Big O е полезна

  • Позволява да се сравняват алгоритми независимо от конкретната машина или език за програмиране.
  • Фокусира се върху поведението при големи входни размери — най-важното при мащабируеми системи.
  • Помага да се откроят алгоритми, които стават неприложими при растеж на данните (напр. факториални или експоненциални сложностите).

Практически съвети

  • При малки n константните фактори и оптимизациите могат да са по-важни; при големи n асимптотичната сложност доминира.
  • Опитайте да намалите степента на n (например от n^2 към n log n или n) — това обикновено дава най-голямо подобрение.
  • Анализирайте както времето, така и паметта, защото спестяването на време често увеличава използваната памет и обратно.

Big O остава основен инструмент в алгоритмиката и софтуерното инженерство за оценка и избор на подходящи решения спрямо очаквания размер на данните и изискванията за производителност.