
Если вы окунулись в мир компьютерных наук и программирования, возможно, вы сталкивались с термином «Большая нотация О». Эта концепция часто пугает новичков, но не бойтесь! В этой статье мы объясним нотацию Big O, почему она важна и как ее интерпретировать.
Что такое нотация большого O?
Нотация Big O — это способ анализа и описания эффективности алгоритмов. Это помогает нам понять, как производительность алгоритма меняется по мере увеличения размера входных данных.
Представьте, что вам нужно выполнить задачу, например, найти определенное число в массиве. Теперь предположим, что у вас есть разные алгоритмы для решения этой задачи. Нотация Big O позволяет нам сравнить эти алгоритмы и понять, как их производительность зависит от размера списка.
Рассмотрим два алгоритма поиска целевого числа в массиве из 12 элементов.
int[] array = { 1, 2, 3, 7, 9, 10, 15, 21, 24, 29, 30, 50 };
int target = 21;
Алгоритм 1
Он просматривает каждое число в массиве одно за другим, пока не найдет целевое число.
public static bool LinearSearch(int[] array, int target)
{
for (int i = 0; i < array.Length; i++)
{
if (array[i] == target)
{
return true;
}
}
return false;
}
Алгоритм 2
Он многократно делит массив на две половины, пока либо не найдет целевое число, либо не определит, что оно отсутствует.
public static bool BinarySearch(int[] array, int target)
{
int left = 0;
int right = array.Length - 1;
while (left <= right)
{
int middle = left + (right - left) / 2;
if (array[middle] == target)
{
return true;
}
if (array[middle] < target)
{
left = middle + 1;
}
else
{
right = middle - 1;
}
}
return false;
}
Теперь давайте проанализируем производительность этих алгоритмов, используя нотацию Big O.
Алгоритм 1 — O(n) линейного времени
Это означает, что время выполнения Алгоритма 1 растет линейно с размером входных данных. Если размер массива удваивается, алгоритм выполняется примерно в два раза дольше. «n» представляет размер ввода.
В нашем примере выше, чтобы найти цель номер 21, потребовалось 8 итераций.
Алгоритм 2 — логарифмическое время O(log n)
Это указывает на то, что время выполнения алгоритма 2 логарифмически растет с размером входных данных. По мере увеличения массива время выполнения алгоритма увеличивается, но гораздо медленнее, чем линейный рост.
Важно отметить, что нотация Big O фокусируется на наихудшем сценарии, описывая верхнюю границу производительности алгоритма. Это помогает нам понять, как алгоритм масштабируется по мере увеличения размера входных данных, но может не давать точных сведений о фактическом времени выполнения.
Как правило, мы хотим выбирать алгоритмы с меньшей сложностью Big O (например, O (1), O (log n), O (n)), поскольку они более эффективны для больших входных данных.
В нашем примере выше, чтобы найти цель номер 21, потребовалось всего 4 итерации.
Вот другие часто используемые обозначения Big O
O(1) — постоянное время
Это означает, что время выполнения алгоритма или потребность в пространстве не зависят от размера входных данных. Он работает одинаково, независимо от размера входных данных.
Примеры включают доступ к элементу массива или выполнение основных арифметических операций.
public static void ConstantAlgorithm(int[] array)
{
int firstElement = array[0];
}
O(n²) — квадратичное время
Квадратичная временная сложность означает, что производительность алгоритма прямо пропорциональна квадрату размера входных данных.
Вложенные циклы типичны для квадратичной временной сложности.
public static void QuadraticAlgorithm(int[] array)
{
int lenght = array.Length;
for (int i = 0; i < lenght; i++)
{
for (int j = 0; j < lenght; j++)
{
// Do some stuff
}
}
}
O(2^n) — экспоненциальное время
Экспоненциальная временная сложность относится к алгоритмам, время выполнения которых экспоненциально растет с размером входных данных. Эти алгоритмы часто включают рекурсивные вызовы или вложенные циклы, что приводит к значительному увеличению количества операций по мере увеличения размера входных данных.
Он быстро становится неэффективным, и его следует избегать, если это возможно.
Пример включает рекурсивный Фибоначчи.
public static int Fibonacci(int n)
{
if (n <= 1)
{
return n;
}
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
В приведенном выше коде функция Фибоначчи рекурсивно вызывает себя дважды с входными данными n-1 и n-2, пока не достигнет базовых случаев, когда n равно 0 или 1. Количество рекурсивных вызовов и операций растет экспоненциально по мере увеличения n, что приводит к к экспоненциальной временной сложности.
Интерпретация нотации Big O
При анализе алгоритмов мы ориентируемся на доминирующий термин или термин с наибольшей скоростью роста. Например, если алгоритм имеет временную сложность O(n² + n), мы игнорируем член n и классифицируем его как O(n²).
Последние мысли
Когда мы пишем код, мы хотим, чтобы он был быстрым и использовал как можно меньше ресурсов. Нотация Big O позволяет нам сравнивать различные алгоритмы и определять, какой из них более эффективен для решения конкретной задачи.
Спасибо за прочтение!
В своих статьях на Medium я делюсь мыслями о веб-разработке, советами по карьере и последними тенденциями в области технологий. Присоединяйтесь ко мне, когда мы вместе исследуем эти захватывающие темы. Давайте учиться, расти и творить вместе!
👏Похлопайте, чтобы статья распространилась
➕Ещё статьи на тему Программирование, карьера и технические тренды.