Нотация "O" (Big O notation)

Материал из m6a
Перейти к: навигация, поиск

Нотация "О" (Big O notation) — это математическая нотация, которая описывает предельное поведение функции, когда аргумент стремится к определенному значению или бесконечности. В информатике нотация "О" используется для классификации алгоритмов по их производительности или сложности в зависимости от размера входных данных. Точнее, "O" определяет верхнюю границу роста времени выполнения алгоритма или использования памяти в худшем случае.

Основные понятия

  1. Временная сложность: Описывает, как время выполнения алгоритма растет с увеличением размера входных данных.
  2. Пространственная сложность: Описывает, как объем используемой памяти алгоритмом растет с увеличением размера входных данных.

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

Функция $f(n)$ имеет порядок $O(g(n))$, если существуют положительные константы $c$ и $n_0$ такие, что для всех $n > n_0$ выполняется неравенство: $$ 0 \le f(n) \le c \cdot g(n) $$

Это означает, что $f(n)$ растет не быстрее, чем $g(n)$ с точностью до постоянного множителя $c$, начиная с некоторого значения $n_0$.

Распространенные классы сложности

  1. O(1): Константная сложность. Время выполнения не зависит от размера входных данных.
    1. Пример: Доступ к элементу массива по индексу.
  1. O(log n): Логарифмическая сложность. Время выполнения растет логарифмически с увеличением размера входных данных.
    1. Пример: Бинарный поиск в отсортированном массиве.
  1. O(n): Линейная сложность. Время выполнения растет линейно с увеличением размера входных данных.
    1. Пример: Линейный поиск в массиве.
  1. O(n log n): Линейно-логарифмическая сложность.
    1. Пример: Быстрая сортировка (в среднем случае), сортировка слиянием.
  1. O(n^2): Квадратичная сложность. Время выполнения растет квадратично с увеличением размера входных данных.
    1. Пример: Сортировка выбором, сортировка пузырьком.
  1. O(n^3): Кубическая сложность. Время выполнения растет кубически с увеличением размера входных данных.
    1. Пример: Умножение матриц.
  1. O(2^n): Экспоненциальная сложность. Время выполнения растет экспоненциально с увеличением размера входных данных.
    1. Пример: Решение задачи коммивояжера методом перебора.
  1. O(n!): Факториальная сложность. Время выполнения растет факториально с увеличением размера входных данных.
    1. Пример: Генерация всех перестановок элементов массива.

Примеры и объяснения

  1. O(1) - Константная сложность:
    1. Если алгоритм всегда выполняет одну и ту же операцию, независимо от размера входных данных, то его сложность $O(1)$.
    2. ```javascript
    3. function getFirstElement(arr) {
    4. return arr[0];
    5. }
    6. ```
  2. O(n) - Линейная сложность:
    1. Если алгоритм должен пройти через каждый элемент входных данных один раз, то его сложность $O(n)$.
    2. ```javascript
    3. function findElement(arr, target) {
    4. for (let i = 0; i < arr.length; i++) {
    5. if (arr[i] === target) {
    6. return i;
    7. }
    8. }
    9. return -1;
    10. }
    11. ```
  3. O(n^2) - Квадратичная сложность:
    1. Если алгоритм должен пройти через все пары элементов во входных данных, то его сложность $O(n^2)$.
    2. ```javascript
    3. function compareAllPairs(arr) {
    4. for (let i = 0; i < arr.length; i++) {
    5. for (let j = 0; j < arr.length; j++) {
    6. console.log(arr[i], arr[j]);
    7. }
    8. }
    9. }
    10. ```
  4. O(log n) - Логарифмическая сложность:
    1. Если алгоритм уменьшает размер входных данных вдвое на каждом шаге, то его сложность $O(\log n)$.
    2. ```javascript
    3. function binarySearch(arr, target) {
    4. let left = 0;
    5. let right = arr.length - 1;
    6. while (left <= right) {
    7. let mid = Math.floor((left + right) / 2);
    8. if (arr[mid] === target) return mid;
    9. if (target < arr[mid]) right = mid - 1;
    10. else left = mid + 1;
    11. }
    12. return -1;
    13. }
    14. ```

Правила упрощения

  1. Правило доминирования: Если функция представляет собой сумму нескольких слагаемых, то учитывается только слагаемое с наибольшим порядком роста.
    1. Пример: $O(n^2 + n) = O(n^2)$
  2. Правило умножения констант: Постоянные множители игнорируются.
    1. Пример: $O(5n) = O(n)$
  3. Правило суммирования последовательностей: Если алгоритм состоит из последовательности шагов, то общая сложность определяется шагом с наибольшей сложностью.

Практическое значение

Нотация "О" позволяет сравнивать алгоритмы и выбирать наиболее эффективный для решения конкретной задачи. Она помогает оценить, как будет изменяться время выполнения или использование памяти при увеличении входных данных, и позволяет принимать обоснованные решения при проектировании программного обеспечения.

Важные замечания

  1. Нотация "О" описывает только верхнюю границу сложности. Фактическое время выполнения может быть меньше.
  2. Нотация "О" не учитывает константы и незначительные члены, которые могут быть важны для небольших размеров входных данных.
  3. Нотация "О" является асимптотической, то есть описывает поведение алгоритма при больших размерах входных данных.