Времени работы алгоритма tmin n соответствует

Рассматривая
различные алгоритмы решения одной и
той же задачи, по­лезно проанализировать,
сколько вычислительных ресурсов они
требуют (время работы, память), и выбрать
наиболее эффективный. Конечно, надо
договориться о том, какая модель
вычислений используется. В данном
учебном пособии в качестве модели по
большей части используется обычная
однопроцессорная машина
с произвольным доступом

(randomaccess
machine,
RAM),
не предусматривающая параллельного
выполнения операций.

Под
временем
работы

(running
time)
алгоритма будем подразумевать число
элементар­ных шагов, которые он
выполняет. Положим, что одна строка
псевдокода требует не более чем
фиксированного числа операций (если
только это не словесное описание каких-то
сложных действий – типа «отсортировать
все точки по x-координате»).
Следует также различать вызов
(call)
процедуры (на который уходит фиксированное
число операций) и её исполнение
(execution),
которое может быть долгим.

Сложность
алгоритма – это величина, отражающая
порядок величины требуемого ресурса
(времени или дополнительной памяти) в
зависимости от размерности задачи.

Таким
образом, будем различать временную T(n)
и пространственную V(n)
сложности алгоритма. При рассмотрении
оценок сложности, будем использовать
только временную сложность. Пространственная
сложность оценивается аналогично. Самый
простой способ оценки – экспериментальный,
то есть запрограммировать алгоритм и
выполнить полученную программу на
нескольких задачах, оценивая время
выполнения программ. Однако, этот способ
имеет ряд недостатков. Во-первых,
экспериментальное программирование –
это, возможно, дорогостоящий процесс.
Во-вторых, необходимо учитывать, что на
время выполнения программ влияют
следующие факторы:

  1. Временная
    сложность алгоритма программы;

  2. Качество
    скомпилированного кода исполняемой
    программы;

  3. Машинные
    инструкции, используемые для выполнения
    программы.

Наличие
второго и третьего факторов не позволяют
применять типовые единицы измерения
временной сложности алгоритма (секунды,
миллисекунды и т.п.), так как можно
получить самые различные оценки для
одного и того же алгоритма, если
использовать разных программистов
(которые программируют алгоритм каждый
по-своему), разные компиляторы и разные
вычислительные машины.

Часто,
временная сложность алгоритма зависит
от количества входных данных. Обычно
говорят, что временная сложность
алгоритма имеет порядок T(n)
от входных данных размера n.
Точно определить величину T(n)
на практике представляется довольно
трудно. Поэтому прибегают к асимптотическим
отношениям с использованием

O-символики.

Существует
метод, позволяющий теоретически оценить
время выполнения алгоритма, который
будет рассмотрен далее.

Листинг
1.3 – Псевдокод алгоритма сортировки
вставками с оценками времени выполнения

Для
вычисления суммарного времени выполнения
процедуры Insertion-Sort
отметим
около каждой стро­ки её стоимость
(число операций) и число раз, которое
эта строка исполняется. Для каждого j
от
2 до п
(здесь
п
=
length[A]

размер массива) требуется подсчитать,
сколько раз будет исполнена строка 5,
обозначим это число через tj.
Строки
внутри цикла выполняются на один раз
меньше, чем проверка, поскольку последняя
проверка выводит из цикла. Строка
стоимостью c,
повторённая т
раз,
даёт вклад c
m
в
общее число операций (однако, это
выражение нельзя использовать для
оценки количества использованной
памяти). Сложив вклады всех строк, получим

Время
работы процедуры зависит не только от
п
но
и от того, какой именно массив размера
п
подан
ей на вход. Для процедуры Insertion-Sort
наиболее
благоприятен случай, когда массив уже
отсортиро­ван. Тогда цикл в строке 5
завершается после первой же проверки
(поскольку A[i]

key
при
i
=
j

1), так что все tj
равны
1, и общее время есть

Таким
образом, в наиболее благоприятном случае
время T(n),
необходимое для сортировки массива
размера п,
является
линейной функцией (linear
function)
от n,
т.е. имеет вид Т(п)
=
a  n b
для некоторых констант a
и
b.
Эти константы определяются выбранными
значениями с1,…,
с8.

Если же массив
расположен в обратном (убывающем)
порядке, время работы

процедуры
будет максимальным: каждый элемент A[j]
придётся
сравнить со всеми элементами А[1],…,
A[j

1]. При этом tj
=
j.
Поскольку

получаем, что в
худшем случае время работы процедуры
равно

В
данном
случае
T(n)
квадратичная
(quadratic
function
),
т.е.
имеет
вид
Т(п= an2
+ b
n
+
с.
Константы
ab и
с
здесь
также определяются значениями с1,…,с8.

Обычно
говорят, что временная сложность
алгоритма имеет порядок T(n)
от входных данных размера n.
Точно определить величину T(n)
на практике представляется довольно
трудно. Поэтому прибегают к асимптотическим
отношениям с использованием O-символики.

Например,
если число тактов (действий), необходимое
для работы алгоритма, выражается как
16n2 + 12n log n + 2n + 3,
то это алгоритм, для которого T(n)
имеет порядок O(n2).
При формировании асимптотической
O-оценки
из всех слагаемых исходного выражения
оставляется одно, вносящее наибольший
вклад при больших n
(остальными слагаемыми можно пренебречь)
и игнорируется коэффициент перед ним
(так как все асимптотические оценки
даются с точностью до константы).

Когда
используют обозначение O(),
имеют в виду не точное время исполнения,
а только его предел сверху, причем с
точностью до постоянного множителя.
Когда говорят, например, что алгоритму
требуется время порядка O(n2),
имеют в виду, что время исполнения задачи
растет не быстрее, чем квадрат количества
элементов.

Таблица 1.2
– Сравнительный анализ скорости роста
функций

1

0

0

1

16

4

64

256

256

8

2 048

65 536

4 096

12

49 152

16 777 216

65 536

16

1 048 565

4 294 967 296

1 048 476

20

20 969 520

1 099 301 922 576

16 775 616

24

402 614 784

281 421 292 179 456

Рисунок
1.1 – Примеры различных функциональных
зависимостей

Если
считать, что числа, приведенные в
таблице 1.2,
соответствуют микросекундам, то для
задачи с 1048476 элементами алгоритму со
временем работы T(log n)
потребуется 20 микросекунд, а алгоритму
со временем работы T(n2)
– более 12 дней.

Если
операция выполняется за фиксированное
число шагов, не зависящее от количества
данных, то принято писать O(1).

Следует
обратить внимание, что основание
логарифма в асимптотических оценках
не пишется. Причина этого весьма проста.
Пусть есть O(log2 n).
Но log= log/ log2,
а log2,
как и любую константу, символ О()
не учитывает. Таким образом, O(logn)
= O(logn).
К любому основанию можно перейти
аналогично, а, значит, и писать его не
имеет смысла.

Практически
время выполнения алгоритма зависит не
только от количества входных данных,
но и от их значений, например, время
работы некоторых алгоритмов сортировки
значительно сокращается, если первоначально
данные частично упорядочены, тогда как
другие методы оказываются нечувствительными
к этому свойству. Чтобы учитывать этот
факт, полностью сохраняя при этом
возможность анализировать алгоритмы
независимо от данных, различают:

  • максимальную
    сложность Tmax(n),
    или сложность наиболее неблагоприятного
    случая, когда алгоритм работает дольше
    всего;

  • среднюю
    сложность Tmid(n)
    – сложность алгоритма в среднем;

  • минимальную
    сложность Tmin(n)
    – сложность в наиболее благоприятном
    случае, когда алгоритм справляется
    быстрее всего.

Теоретическая
оценка временной сложности алгоритма
осуществляется с использованием
следующих базовых принципов:

  1. Время
    выполнения операций присваивания,
    чтения, записи обычно имеют порядок
    O(1).
    Исключением являются операторы
    присваивания, в которых операнды
    представляют собой массивы или вызовы
    функций;

  2. Время
    выполнения последовательности операций
    совпадает с наибольшим временем
    выполнения операции в данной
    последовательности (правило сумм: если
    T1(n)
    имеет порядок O(f(n)),
    а T2(n)
    – порядок O(g(n)),
    то T1(n)
    + T2(n)
    имеет порядок O(max(f(n),
    g(n)));

  3. Время
    выполнения конструкции ветвления
    (if-then-else)
    состоит из времени вычисления логического
    выражения (обычно имеет порядок O(1))
    и наибольшего из времени, необходимого
    для выполнения операций, исполняемых
    при истинном значении логического
    выражения и при ложном значении
    логического выражения;

  4. Время
    выполнения цикла состоит из времени
    вычисления условия прекращения цикла
    (обычно имеет порядок O(1) )
    и произведения количества выполненных
    итераций цикла на наибольшее возможное
    время выполнения операций тела цикла.

  5. Время
    выполнения операции вызова процедур
    определяется как время выполнения
    вызываемой процедуры;

  6. При
    наличии в алгоритме операции безусловного
    перехода, необходимо учитывать изменения
    последовательности операций,
    осуществляемых с использованием этих
    операции безусловного перехода.

Итак,
время работы в худшем случае и в лучшем
случае могут
сильно
различаться. При анализе алгоритмов
наиболее часто используется время
работы в худшем случае

(worstcase
running
time),
которое определяется как максимальное
время работы для входов данного размера.
Почему? Вот несколько причин.

  1. Зная
    время работы в худшем случае можно
    гарантировать, что выполнение
    алгоритма
    закончится за некоторое время при любом
    входе данного размера;

  2. На
    практике «плохие» входы (для которых
    время работы близко к максимуму)
    встречаются наиболее часто. Например,
    для базы данных плохим запросом может
    быть поиск отсутствующего элемента
    (очень распространенная ситуация);

  3. Время
    работы в среднем может быть до­вольно
    близко к времени работы в худшем случае.
    Пусть, например, сортируется массив
    из п
    случайных
    чисел
    с помощью процедуры Insertion-Sort.Сколько
    раз придётся выполнить цикл в строках
    5-8 (листинг 1.3)? В среднем около половины
    элементов массива A[1..j
    1]
    больше A[j],
    так
    что tj
    в
    среднем можно считать равным j/2,
    и
    время Т(п)
    квадратично
    зависит от n.

В
некоторых случаях требуется также
среднее время
работы

(averagecase
running
time,
expected
running
time)
алгоритма на входах данной длины.
Конечно, эта величина зависит от
выбранного распределения вероятно­стей,
и на практике реальное распределение
входов может отличаться от пред­полагаемого,
которое обычно считают равномерным.
Иногда можно добиться равномерности
распределения, используя датчик случайных
чисел.

Автор: PythonInDepth

Когда в детстве меня учили умножать числа, мне говорили, что смысл умножения в том, чтобы короче записать сумму. Например, 4 * 3 это то же, что 4 + 4 + 4.

Сведение умножения к сумме — самый простой, наивный алгоритм умножения. А теперь я возьму мой рабочий ноут и попробую перемножить этим способом какие-нибудь большие числа, например, 4 * 10000000000:

sum = 0
for i in range(0, 10000000000):
    sum += 4

print(sum)

Если я попробую посчитать то же самое на калькуляторе, то замечу, что лаптоп отрабатывает заметно медленнее, несмотря на то, что он мощнее. Почему? Потому что для умножения чисел существует несколько алгоритмов, а я выбрала самый неэффективный из них. Ещё есть, например, метод Карацубы, или алгоритм на основе преобразования Фурье. В калькуляторах обычно используется второй, и он требует значительно меньше операций, чем наивный.

Как понять, быстро ли работает алгоритм? Можно попробовать измерить время напрямую:

import time
start = time.time()
my_awesome_alg()
end = time.time()
print(end - start)

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

Чтобы уйти от физических свойств вычислителя, используют понятие сложности. Асимптотическая сложность алгоритма — это то, как изменяется время исполнения в зависимости от объема входных данных. В этом определении намеренно не учитывается то, на каком устройте выполняется алгоритм: это может быть компьютер, калькулятор или даже человек. Такой абстрактный вычислитель называют машиной Тьюринга.

При этом разделяют сложность алгоритма в худшем и лучшем случае. Представьте, что мы хотим проверить, есть ли в списке число 42. Мы проверяем первый элемент на равенство 42, второй, третий… В лучшем случае первый элемент окажется искомым. В худшем — последний. Поэтому обычно рассматривают сложность алгоритма в наилучшем, наихудшем случае, и в среднем. Как правило, пессимистическая оценка самая интересная из всех, потому что позволяет планировать объем ресурсов.

Временную сложность алгоритма в худшем случае выражают с использованием нотации «O» большое. В этом случае из рассмотрения исключают члены меньшего порядка и говорят об изменении используемых ресурсов при стремлении размера входа к бесконечности. Например, если время, которое нужно алгоритму для выполнения работы, для всех входов длины n не превосходит 5n^3 + 3n, то асимптотическая временная сложность равна O(n^3).

Рассмотрим несколько примеров

O(1)

Время, которое не зависит от объема входных данных, или постоянное время. За постоянное время можно получить элемент массива по индексу или добавить элемент в связный список.

O(n)

Алгоритм, в котором мы идём по списку, чтобы узнать, есть ли в нем значение 42, в худшем случае требует O(n) операций, где n — длина списка. Такую же сложность имеет сравнение строк, потому что по сути оно тоже требуют обхода всей строки.

O(log n)

Проверить, есть ли значение 42 в списке, можно быстрее, чем за O(n), если список отсортирован. Тогда можно проверить на равенство элемент из середины списка. Если он равен 42, то останавливаемся. Если больше — значит, слева можно не искать, там значения только меньше. Будем продолжать проверку, выбирая каждый раз элемент из середины оставшегося отрезка. Этот алгоритм называют бинарным поиском и он имеет логарифмическую сложность, потому что количество вариантов уменьшается каждый раз на 2, как функция, обратная степенной (она же логарифм).

O(n log n)

Можно показать, что большинство алгоритмов сортировки имеют сложность n log(n). За время n log(n) работают сортировка слиянием, кучей, и быстрая сортировка. Еще есть теорема, которая говорит, что если сортировка основана на сравнении элементов, то быстрее, чем за n log(n) отсортировать элементы не получится.

O(n^2)

За время n^2 работает обход двумерного массива, это можно представить себе как обход таблицы n * n. И ещё за это же время работают некоторые не очень эффективные по времени алгоритмы сортировки, например, сортировка пузырьком.

Для разработчика важно иметь интуицию насчет временной сложности алгоритмов, потому что за счет неэффективности вычислений можно загубить производительность самого мощного железа, как это случилось, когда калькулятор побил мой мощный ноутбук. Поэтому этой науке уделяется большое внимание в разработке. 95%, что на собеседовании вам предложат алгоритмическую задачу и попросят оценить время выполнения вашего решения.

Если вы хотите углубиться в теорию алгоритмов, то я советую специализацию Алгоритмы и структуры данных на Курсере. Теория здесь изложена доступно и в курсе есть задачи, чтобы набить руку. А еще есть Стенфордский учебник по теории сложности, он написан замечательным языком и в нем прекрасные примеры.

В информатике временна́я сложность алгоритма определяет время работы, используемое алгоритмом, как функции от длины строки, представляющей входные данные [1]. Временная сложность алгоритма обычно выражается с использованием нотации «O» большое, которая исключает коэффициенты и члены меньшего порядка. Если сложность выражена таким способом, говорят об асимптотическом описании временной сложности, т.е. при стремлении размера входа к бесконечности. Например, если время, которое нужно алгоритму для выполнения работы, для всех входов длины n не превосходит 5n3 + 3n для некоторого n (большего некоторого n0), асимптотическая временная сложность равна O (n3).

Временная сложность зачастую оценивается путём подсчёта числа элементарных операций, осуществляемых алгоритмом, где элементарная операция занимает для выполнения фиксированное время. Тогда полное время выполнения и число элементарных операций, выполненных алгоритмом, отличаются максимум на постоянный множитель.

Поскольку производительность алгоритма может отличаться при входах одного и того же размера, обычно используется временная сложность наихудшего случая[en] поведения алгоритма, которая обозначается как T(n) и которая определяется как максимальное время, которое требуется для любого входа длины n. Реже, и это обычно оговаривается специально, время измеряется как средняя сложность[en]. Сложность по времени классифицируется природой функции T(n). Например, алгоритм с T(n) = O(n) называется алгоритмом линейного времени, а об алгоритме с T(n) = O(Mn) и mn= O(T(n)) для некоторого Mm > 1 говорят как об алгоритме с экспоненциальным временем.

Содержание

  • 1 Таблица сложностей по времени
  • 2 Постоянное время
  • 3 Логарифмическое время
  • 4 Полилогарифмическое время
  • 5 Сублинейное время
  • 6 Линейное время
  • 7 Квазилинейное время
    • 7.1 Линейно-логарифмическое время
  • 8 Подквадратичное время
  • 9 Полиномиальное время
    • 9.1 Строго и слабо полиномиальное время
    • 9.2 Классы сложности
  • 10 Суперполиномиальное время
  • 11 Квазиполиномиальное время
    • 11.1 Связь с NP-полными задачами
  • 12 Субэкспоненциальное время
    • 12.1 Первое определение
    • 12.2 Второе определение
      • 12.2.1 Гипотеза об экспоненциональном времени
  • 13 Экспоненциальное время
  • 14 Двойное экспоненциональное время
  • 15 Смотрите также
  • 16 Примечания

Таблица сложностей по времени

Следующая таблица суммирует некоторые, обычно рассматриваемые, классы сложности. В таблице poly(x) = xO(1), т.е. многочлен от x.

Название Класс сложности Время работы (T(n)) Примеры времени работы Примеры алгоритмов
постоянное время O(1) 10 Определение чётности целого числа (представленного в двоичном виде)
обратная функция Аккермана от времени O(α(n)) Амортизационный анализ[en] на одну операцию с использованием непересекающихся множеств
повторно логарифмическое время O(log* n) Распределённые раскраски циклов
дважды логарифмическое O(log log n) Время амортизации на одну операцию при использовании ограниченной очереди с приоритетами[en][2]
логарифмическое время DLOGTIME[en] O(log n) log n, log(n2) Двоичный поиск
полилогарифмическое время poly(log n) (log n)2
дробная степень O(nc) при 0 < c < 1 n1/2, n2/3 Поиск в k-мерном дереве
линейное время O(n) n Поиск наименьшего или наибольшего элемента в неотсортированном массиве
«n log звёздочка n» время O(n log* n) Алгоритм триангуляции многоугольника[en] Зайделя[en].
линейно-логарифмическое время O(n log n) n log n, log n! Максимально быстрая сортировка сравнением[en]
квадратичное время O(n2) n2 Сортировка пузырьком, сортировка вставками, прямая свёртка[en]
кубическое время O(n3) n3 Обычное умножение двух n×n матриц. Вычисление Частичная корреляция[en].
полиномиальное время P 2O(log n) = poly(n) n, n log n, n10 Алгоритм Кармаркара для линейного программирования, тест простоты числа АКС
квазиполиномиальное время QP 2poly(log n) nlog log n, nlog n Наиболее известный

O(log2 n)-Аппроксимационный алгоритм для ориентированной задачи Штайнера.

подэкспоненциальное время
(первое определение)
SUBEXP O(2nε) for all ε > 0 O(2log nlog log n) Если принять теоретические гипотезы, BPP содержится в SUBEXP.[3]
подэкспоненциальное время
(второе определение)
2o(n) 2n1/3 Наиболее известные алгоритмы разложения на множители целых чисел и изоморфизма графов[en]
экспоненциальное время
(с линейной экспонентой)
E[en] 2O(n) 1.1n, 10n Решение задачи коммивояжёра с помощью динамического программирования
экспоненциальное время EXPTIME 2poly(n) 2n, 2n2 Решение задачи о порядке перемножения матриц с помощью полного перебора
факториальное время O(n!) n! Решение задачи коммивояжёра полным перебором
дважды экспоненциальное время 2-EXPTIME[en] 22poly(n) 22n Проверка верности заданного утверждения в арифметике Пресбургера

Постоянное время

Говорят, что алгоритм является алгоритмом постоянного времени (записывается как время O(1)), если значение T(n) ограничено значением, не зависящим от размера входа. Например, получение одного элемента в массиве занимает постоянное время, поскольку выполняется единственная команда для его обнаружения. Однако нахождение минимального значения в несортированном массиве не является операцией с постоянным временем, поскольку мы должны просмотреть каждый элемент массива. Таким образом, эта операция занимает линейное время, O(n). Если число элементов известно заранее и не меняется, о таком алгоритме можно говорить как об алгоритме постоянного времени.

Несмотря на название «постоянное время», время работы не обязательно должно быть независимым от размеров задачи, но верхняя граница времени работы не должна зависеть. Например, задача «обменять значения a и b, если необходимо, чтобы в результате получили ab«, считается задачей постоянного времени, хотя время работы алгоритма может зависеть от того, выполняется ли уже неравенство ab или нет. Однако существует некая константа t, для которой время выполнения задачи всегда не превосходит t.

Ниже приведены некоторые примеры кода, работающие за постоянное время:

int index = 5;
int item = list[index];
if (условие верно) then
   выполнить некоторые операции с постоянным временем работы
else
   выполнить некоторые операции с постоянным временем работы
for i = 1 to 100
   for j = 1 to 200
      выполнить некоторые операции с постоянным временем работы

Если T(n) равен O(некоторое постоянное значение), это эквивалентно T(n) равно O(1).

Логарифмическое время

Говорят, что алгоритм выполняется за логарифмическое время, если T(n) = O(log n). Поскольку в компьютерах принята двоичная система счисления, в качестве базы логарифма используется 2 (то есть, log2 n). Однако при замене базы[en] логарифмы loga n и logb n отличаются лишь на постоянный множитель, который в записи O-большое отбрасывается. Таким образом, O(log n) является стандартной записью для алгоритмов логарифмического времени независимо от базы логарифма.

Алгоритмы, работающие за логарифмическое время, обычно встречаются при операциях с двоичными деревьями или при использовании двоичного поиска.

O(log n) алгоритмы считаются высокоэффективными, поскольку время выполнения операции в пересчёте на один элемент уменьшается с увеличением числа элементов.

Очень простой пример такого алгоритма — деление строки пополам, вторая половина опять делится пополам, и так далее. Это занимает время O(log n) (где n — длина строки, мы здесь полагаем, что console.log и str.substring занимают постоянное время).
Это означает, что для увеличения числа печатей необходимо удвоить длину строки.

// Функция для рекурсивной печати правой половины строки
var right = function(str)
{
    var length = str.length;
    
    // вспомогательная функция
    var help = function(index)
    {
       // Рекурсия: печатаем правую половину
        if(index < length)
        {
          
            // Печатаем символы от index до конца строки
            console.log(str.substring(index, length));
            
            // рекурсивный вызов: вызываем вспомогательную функцию с правой частью
            help(Math.ceil((length + index)/2));
        }
    }
    help(0);
}

Полилогарифмическое время

Говорят, что алгоритм выполняется за полилогарифмическое время, если T(n) = O((log n)k), для некоторого k. Например, задача о порядке перемножения матриц может быть решена за полилогарифмическое время на параллельной РАМ-машине[en][4].

Сублинейное время

Говорят, что алгоритм выполняется за сублинейное время, если T(n) = o(n). В частности, сюда включаются алгоритмы с временной сложностью, перечисленные выше, как и другие, например, поиск Гровера со сложностью O(n½).

Типичные алгоритмы, которые, являясь точными, всё же работают за сублинейное время, используют распараллеливание процессов (как это делают алгоритм NC1 вычисления определителя матрицы), неклассические вычисления (как в поиске Гровера) или имеют гарантированное предположение о струтуре входа (как работающие за логарифмическое время, алгоритмы двоичного поиска и многие алгоритмы обработки деревьев). Однако формальные конструкции, такие как множество всех строк, имеющие бит 1 в позиции, определяемой первыми log(n) битами строки, могут зависеть от каждого бита входа, но, всё же, оставаться сублинейными по времени.

Термин алгоритм с сублинейным временем работы обычно используется для алгоритмов, которые, в отличие от приведённых выше примеров, работают на обычных последовательных моделях машин и не предполагают априорных знаний о структуре входа [5]. Однако для них допускается применение вероятностных методов и даже более того, алгоритмы должны быть вероятностными для большинства тривиальных задач.

Поскольку такой алгоритм обязан давать ответ без полного чтения входных данных, он в очень сильной степени зависит от способов доступа, разрешённых во входном потоке. Обычно для потока, представляющего собой битовую строку b1,…,bk, предполагается, что алгоритм может за время O(1) запросить значение bi для любого i.

Алгоритмы сублинейного времени, как правило, вероятностны и дают лишь аппроксимированное решение. Алгоритмы сублинейного времени выполнения возникают естественным образом при исследовании проверки свойств[en].

Линейное время

Говорят, что алгоритм работает за линейное время, или O(n), если его сложность равна O(n). Неформально, это означает, что для достаточно большого размера входных данных время работы увеличивается линейно от размера входа. Например, процедура, суммирующая все элементы списка, требует время, пропорциональное длине списка. Это описание не вполне точно, поскольку время работы может существенно отличаться от точной пропорциональности, особенно для малых значений n.

Линейное время часто рассматривается как желательный атрибут алгоритма[6]. Было проведено много исследований для создания алгоритмов с (почти) линейным временем работы или лучшим. Эти исследования включали как программные, так и аппаратные подходы. В случае аппаратного исполнения некоторые алгоритмы, которые, с математической точки зрения, никогда не могут достичь линейного времени исполнения в стандартных моделях вычислений, могут работать за линейное время. Существуют некоторые аппаратные технологии, которые используют параллельность для достижения такой цели. Примером служит ассоциативная память. Эта концепция линейного времени используется в алгоритмах сравнения строк, таких как алгоритм Бойера — Мура и алгоритм Укконена.

Квазилинейное время

Говорят, что алгоритм работает за квазилинейное время, если T(n) = O(n logk n) для некоторой константы k. Линейно-логарифмическое время является частным случаем с k = 1[7]. При использовании обозначения слабое-O эти алгоритмы являются Õ(n). Алгоритмы квазилинейного времени являются также o(n1+ε) для любого ε > 0 и работают быстрее любого полинома от n со степенью, строго большей 1.

Алгоритмы, работающие за квазилинейное время, вдобавок к линейно-логарифмическим алгоритмам, упомянутым выше, включают:

  • Сортировка слиянием на месте, O(n log2 n)
  • Быстрая сортировка, O(n log n), в вероятностной версии имеет линейно-логарифмическое время выполнения в худшем случае. Невероятностная версия имеет линейно-логарифмическое время работы только для измерения сложности в среднем.
  • Пирамидальная сортировка, O(n log n), сортировка слиянием, introsort, бинарная сортировка с помощью дерева, плавная сортировка, пасьянсная сортировка[en], и т.д. в худшем случае
  • Быстрые преобразования Фурье, O(n log n)
  • Вычисление матриц Монжа, O(n log n)

Линейно-логарифмическое время

Линейно-логарифмическое является частным случаем квазилинейного времени с показателем k = 1 на логарифмическом члене.

Линейно-логарифмическая функция — это функция вида n • log n (т.е. произведение линейного[en] и логарифмического членов). Говорят, что алгоритм работает за линейно-логарифмическое время, если T(n) = O(n log n).[8] Таким образом, линейно-логарифмический элемент растёт быстрее, чем линейный член, но медленнее, чем любой многочлен от n со степенью, строго большей 1.

Во многих случаях время работы n • log n является просто результатом выполнения операции Θ(log n) n раз. Например, сортировка с помощью двоичного дерева создаёт двоичное дерево путём вставки каждого элемента в массив размером n один за другим. Поскольку операция вставки в сбалансированное бинарное дерево поиска[en] занимает время O(log n), общее время выполнения алгоритма будет линейно-логарифмическим.

Сортировки сравнением[en] требуют по меньшей мере линейно-логарифмического числа сравнений для наихудшего случая, поскольку log(n!) = Θ(n log n) по формуле Стирлинга. То же время выполнения зачастую возникает из рекуррентного уравнения T(n) = 2 T(n/2) + O(n).

Подквадратичное время

Говорят, что алгоритм выполняется за субквадратичное время, если T(n) = o(n2).

Например, простые, основанные на сравнении, алгоритмы сортировки квадратичны (например, сортировка вставками), но можно найти более продвинутые алгоритмы, которые имеют субквадратичное время выполнения (например, сортировка Шелла). Никакие сортировки общего вида не работают за линейное время, но переход от квадратичного к субквадратичному времени имеет большую практическую важность.

Полиномиальное время

Говорят, что алгоритм работает за полиномиальное время, если время работы ограничено сверху[en] многочленом от размера входа для алгоритма, то есть T(n) = O(nk) для некоторой константы k[1][9]. Задачи, для которых алгоритмы с детерминированным полиномиальным временем существуют, принадлежат классу сложности P, который является центральным в теории вычислительной сложности. Тезис Кобэма[en] утверждает, что полиномиальное время является синонимом понятий «легко поддающийся обработке», «выполнимый», «эффективный» или «быстрый»[10].

Некоторые примеры алгоритмов полиномиального времени:

Строго и слабо полиномиальное время

В некоторых контекстах, особенно в оптимизации, различают алгоритмы со строгим полиномиальным временем и слабо полиномиальным временем. Эти две концепции относятся только ко входным данным, состоящим из целых чисел.

Строго полиномиальное время определяется в арифметической модели вычислений. В этой модели базовые арифметические операции (сложение, вычитание, умножение, деление и сравнение) берутся за единицы выполнения, независимо от длины операндов. Алгоритм работает в строго полиномиальное время, если[11]

  1. число операций в арифметической модели вычислений ограничено многочленом от числа целых во входном потоке, и
  2. память, используемая алгоритмом, ограничена многочленом от размеров входа.

Любой алгоритм с этими двумя свойствами можно привести к алгоритму полиномиального времени путём замены арифметических операций на соответствующие алгоритмы выполнения арифметических операций на машине Тьюринга. Если второе из вышеприведённых требований не выполняется, это больше не будет верно. Если задано целое число 2^{n} (которое занимает память, пропорциональную n в машине Тьюринга), можно вычислить 2^{{2^{n}}} с помощью n операций, используя повторное возведение в степень. Однако память, используемая для представления 2^{{2^{n}}}, пропорциональна 2^{n}, и она скорее экспоненционально, чем полиномиально, зависит от памяти, используемой для входа. Отсюда — невозможно выполнить эти вычисления за полиномиальное время на машине Тьюринга, но можно выполнить за полиномиальное число арифметических операций.

Обратно — существуют алгоритмы, которые работают за число шагов машины Тьюринга, ограниченных полиномиальной длиной бинарно закодированного входа, но не работают за число арифметических операций, ограниченное многочленом от количества чисел на входе. Алгоритм Евклида для вычисления наибольшего общего делителя двух целых чисел является одним из примеров. Для двух целых чисел a и b время работы алгоритма ограничено {displaystyle O((log  a+log  b)^{2})} шагам машины Тьюринга. Это число является многочленом от размера бинарного представления чисел a и b, что грубо можно представить как {displaystyle log  a+log  b}. В то же самое время число арифметических операций нельзя ограничить числом целых во входе (что в данном случае является константой — имеется только два числа во входе). Ввиду этого замечания алгоритм не работает в строго полиномиальное время. Реальное время работы алгоритма зависит от величин a и b, а не только от числа целых чисел во входе.

Если алгоритм работает за полиномиальное время, но не за строго полиномиальное время, говорят, что он работает за слабо полиномиальное время[12].
Хорошо известным примером задачи, для которой известен слабо полиномиальный алгоритм, но не известен строго полиномиальный алгоритм, является линейное программирование. Слабо полиномиальное время не следует путать с псевдополиномиальным временем.

Классы сложности

Концепция полиномиального времени приводит к нескольким классам сложности в теории сложности вычислений. Некоторые важные классы, определяемые с помощью полиномиального времени, приведены ниже.

  • P: Класс сложности задач разрешимости, которые могут быть решены в детерминированной машине Тьюринга за полиномиальное время.
  • NP: Класс сложности задач разрешимости, которые могут быть решены в недетерминированной машине Тьюринга за полиномиальное время.
  • ZPP: Класс сложности задач разрешимости, которые могут быть решены с нулевой ошибкой в вероятностной машине Тьюринга за полиномиальное время.
  • RP: Класс сложности задач разрешимости, которые могут быть решены с односторонними ошибками в вероятностной машине Тьюринга за полиномиальное время.
  • BPP: Класс сложности задач разрешимости, которые могут быть решены с двусторонними ошибками в вероятностной машине Тьюринга за полиномиальное время.
  • BQP: Класс сложности задач разрешимости, которые могут быть решены с двусторонними ошибками в квантовой машине Тьюринга за полиномиальное время.

P является наименьшим классом временной сложности на детерминированной машине, которая является устойчивой[en] в терминах изменения модели машины. (Например, переход от одноленточной машины Тьюринга к мультиленточной может привести к квадратичному ускорению, но любой алгоритм, работающий за полиномиальное время на одной модели, будет работать за полиномиальное время на другой.)

Суперполиномиальное время

Говорят, что алгоритм работает за суперполиномиальное время, если T(n) не ограничен сверху полиномом. Это время равно ω(nc) для всех констант c, где n — входной параметр, обычно — число бит входа.

Например, алгоритм, осуществляющий 2n шагов, для входа размера n требует суперполиномиального времени (конкретнее, экспоненциального времени).

Ясно, что алгоритм, использующий экспоненциальные ресурсы, суперполиномиален, но некоторые алгоритмы очень слабо суперполиномиальны. Например, тест простоты Адлемана — Померанса — Румели[en]* работает за время nO(log log n) на n-битном входе. Это растёт быстрее, чем любой полином, для достаточно большого n, но размер входа должен стать очень большим, чтобы он не доминировался полиномом малой степени.

Алгоритм, требующий суперполиномиального времени, лежит вне класса сложности P. Тезис Кобэма[en] утверждает, что эти алгоритмы непрактичны, и во многих случаях это так. Поскольку задача равенства классов P и NP не решена, никаких алгоритмов для решения NP-полных задач за полиномиальное время в настоящее время не известно.

Квазиполиномиальное время

Алгоритмы квазиполиномиального времени — это алгоритмы, работающие медленнее, чем за полиномиальное время, но не столь медленно, как алгоритмы экспоненциального времени. Время работы в худшем случае для квазиполиномиального алгоритма равно {displaystyle 2^{O((log n)^{c})}} для некоторого фиксированного c. Хорошо известный классический алгоритм разложения целого числа на множители, общий метод решета числового поля, который работает за время около {displaystyle 2^{{tilde {O}}(n^{1/3})}}, не является квазиполиномиальным, поскольку время работы нельзя представить как {displaystyle 2^{O((log n)^{c})}} для некоторого фиксированного c. Если константа «c» в определении алгоритма квазиполиномиального времени равна 1, мы получаем алгоритм полиномиального времени, а если она меньше 1, мы получаем алгоритм сублинейного времени.

Алгоритмы квазиполиномиального времени обычно возникают при сведении NP-трудной задачи к другой задаче. Например, можно взять NP-трудную задачу, скажем, 3SAT, и свести её к другой задаче B, но размер задачи станет равным {displaystyle 2^{O((log n)^{c})}}. В этом случае сведение не доказывает, что задача B NP-трудна, такое сведение лишь показывает, что не существует полиномиального алгоритма для B, если только не существует квазиполиномиального алгоритма для 3SAT (а тогда и для всех NP-задач). Подобным образом — существуют некоторые задачи, для которых мы знаем алгоритмы с квазиполиномиальным временем, но для которых алгоритмы с полиномиальным временем неизвестны. Такие задачи появляются в аппроксимационых алгоритмах. Знаменитый пример — ориентированная задача Штайнера, для которой существует аппроксимационный квазиполиномиальный алгоритм с аппроксимационным коэффициентом {displaystyle O(log ^{3}n)} (где n — число вершин), но существование алгоритма с полиномиальным временем является открытой проблемой.

Класс сложности QP состоит из всех задач, имеющих алгоритмы квазиполиномиального времени. Его можно определить в терминах DTIME следующим образом[13]

{displaystyle {mbox{QP}}=bigcup _{cin mathbb {N} }{mbox{DTIME}}(2^{(log n)^{c}})}

Связь с NP-полными задачами

В теории сложности нерешённая проблема равенства классов P и NP спрашивает, не имеют ли все задачи из класса NP алгоритмы решения за полиномиальное время. Все хорошо известные алгоритмы для NP-полных задач, наподобие 3SAT, имеют экспоненциальное время. Более того, существует гипотеза, что для многих естественных NP-полных задач не существует алгоритмов с субэкспоненциальным временем выполнения. Здесь «субэкспоненциальное время » взято в смысле второго определения, приведённого ниже. (С другой стороны, многие задачи из теории графов, представленные естественным путём матрицами смежности, разрешимы за субэкспоненциальное время просто потому, что размер входа равен квадрату числа вершин.) Эта гипотеза (для задачи k-SAT) известна как гипотеза экспоненциального времени[en][14]. Поскольку она предполагает, что NP-полные задачи не имеют алгоритмов квазиполиномиального времени, некоторые результаты неаппроксимируемости в области аппроксимационных алгоритмов исходят из того, что NP-полные задачи не имеют алгоритмов квазиполиномиального времени. Например, смотрите известные результаты по неаппроксимируемости задачи о покрытии множества.

Субэкспоненциальное время

Термин субэкспоненциальное[en] время используется, чтобы выразить, что время выполнения некоторого алгоритма может расти быстрее любого полиномиального, но остаётся существенно меньше, чем экспоненциальное. В этом смысле задачи, имеющие алгоритмы субэкспоненциального времени, являются более податливыми, чем алгоритмы только с экспотенциальным временем. Точное определение «субэкспоненциальный» пока не является общепринятым[15], и мы приводим ниже два наиболее распространённых определения.

Первое определение

Говорят, что задача решается за субэкспоненциальное время, если она решается алгоритмом, логарифм времени работы которого растёт меньше, чем любой заданный многочлен. Более точно — задача имеет субэкспоненциальное время, если для любого ε > 0 существует алгоритм, который решает задачу за время O(2nε). Множество все таких задач составляет класс сложности SUBEXP, который в терминах DTIME можно выразить как[3][16][17][18].

{displaystyle {text{SUBEXP}}=bigcap _{varepsilon >0}{text{DTIME}}left(2^{n^{varepsilon }}right)}

Заметим, что здесь ε не является частью входных данных и для каждого ε может существовать свой собственный алгоритм решения задачи.

Второе определение

Некоторые авторы определяют субэкспоненциальное время как время работы 2o(n)[14][19][20]. Это определение допускает большее время работы, чем первое определение. Примером такого алгоритма субэкспоненциального времени служит хорошо известный классический алгоритм разложения целых чисел на множители, общий метод решета числового поля, который работает за время около {displaystyle 2^{{tilde {O}}(n^{1/3})}}, где длина входа равна n. Другим примером служит хорошо известный алгоритм для задачи изоморфизма графов[en], время работы которого равно {displaystyle 2^{O({sqrt {(}}nlog n))}}.

Заметим, что есть разница, является ли алгоритм субэкспоненциальным по числу вершин или числу рёбер. В параметризованной сложности[en] эта разница присутствует явно путём указания пары {displaystyle (L,k)}, задачи разрешимости и параметра k. SUBEPT является классом всех параметризованных задач, которые работают за субэкспоненциальное время по k и за полиномиальное по n[21]:

{displaystyle {text{SUBEPT}}={text{DTIME}}left(2^{o(k)}cdot {text{poly}}(n)right).}

Точнее, SUBEPT является классом всех параметризованных задач {displaystyle (L,k)}, для которых существует вычислимая функция {displaystyle f:mathbb {N} to mathbb {N} } с {displaystyle fin o(k)} и алгоритм, который решает L за время {displaystyle 2^{f(k)}cdot {text{poly}}(n)}.

Гипотеза об экспоненциональном времени

Гипотеза об экспоненциональном времени (‘ETH) утверждает, что 3SAT, задача выполнимости булевых формул в конъюнктивной нормальной форме с максимум тремя литералами на предложение и n переменными, не может быть решена за время 2o(n). Точнее, гипотеза говорит, что существует некая константа c>0, такая, что 3SAT не может быть решена за время 2cn на любой детерминированной машине Тьюринга. Если через m обозначить число предложений, ETH эквивалентна гипотезе, что k-SAT не может быть решена за время 2o(m) для любого целого k ≥ 3[22]. Из гипотезы об экспоненциальном времени следует, что P ≠ NP.

Экспоненциальное время

Говорят, что алгоритм работает за экспоненциальное время, если T(n) ограничено сверху значением 2poly(n), где poly(n) — некий многочлен от n. Более формально, алгоритм работает за экспоненциальное время, если T(n) ограничено O(2nk) с некоторой константой k. Задачи, которые выполняются за экспоненциальное время на детерминированных машинах Тьюринга, образуют класс сложности EXP.

{displaystyle {text{EXP}}=bigcup _{cin mathbb {N} }{text{DTIME}}left(2^{n^{c}}right)}

Иногда термин экспоненциальное время используется для алгоритмов, для которых T(n) = 2O(n), где показатель является не более чем линейной функцией от n. Это приводит к классу сложности E[en].

{displaystyle {text{E}}=bigcup _{cin mathbb {N} }{text{DTIME}}left(2^{cn}right)}

Двойное экспоненциональное время

Говорят, что алгоритм выполняется за дважды экспоненциональное[en] время, если T(n) ограничено сверху значением 22poly(n), где poly(n) — некоторый многочлен от n. Такие алгоритмы принадлежат классу сложности 2-EXPTIME[en].

{displaystyle {mbox{2-EXPTIME}}=bigcup _{cin mathbb {N} }{mbox{DTIME}}(2^{2^{n^{c}}})}

К хорошо известным дважды экспоненциальным алгоритмам принадлежат:

  • Процедура вычисления для арифметики Пресбургера
  • Вычисление базиса Грёбнера (в худшем случае[23])
  • Элиминация кванторов в вещественно замкнутых полях[en] требует как минимум дважды экспоненциальное время выполнения [24] и может быть выполнена за это время[25].

Смотрите также

  • L-нотация
  • Сложность по памяти[en]

Примечания

  1. 1 2 Michael Sipser. Introduction to the Theory of Computation. — Course Technology Inc, 2006. — ISBN 0-619-21764-2.
  2. Kurt Mehlhorn, Stefan Naher. Bounded ordered dictionaries in O(log log N) time and O(n) space // Information Processing Letters. — 1990. — Т. 35, вып. 4. — С. 183. — DOI:10.1016/0020-0190(90)90022-P.
  3. 1 2 László Babai, Lance Fortnow, N. Nisan, Avi Wigderson. BPP has subexponential time simulations unless EXPTIME has publishable proofs // Computational Complexity. — Berlin, New York: Springer-Verlag, 1993. — Т. 3, вып. 4. — С. 307–318. — DOI:10.1007/BF01275486.
  4. J. E. Rawlins, Gregory E. Shannon. Efficient Matrix Chain Ordering in Polylog Time // SIAM Journal on Computing. — Philadelphia: Society for Industrial and Applied Mathematics, 1998. — Т. 27, вып. 2. — С. 466–490. — ISSN 1095-7111. — DOI:10.1137/S0097539794270698.
  5. Ravi Kumar, Ronitt Rubinfeld. Sublinear time algorithms // SIGACT News. — 2003. — Т. 34, вып. 4. — С. 57–67. — DOI:10.1145/954092.954103.
  6. DR K N PRASANNA KUMAR, PROF B S KIRANAGI AND PROF C S BAGEWADI. A GENERAL THEORY OF THE SYSTEM ‘QUANTUM INFORMATION — QUANTUM ENTANGLEMENT, SUBATOMIC PARTICLE DECAY – ASYMMETRIC SPIN STATES, NON LOCALLY HIDDEN VARIABLES – A CONCATENATED MODEL // International Journal of Scientific and Research Publications. — July 2012. — Т. 2, вып. 7. — ISSN 22503153.
  7. Ashish V. Naik, Kenneth W. Regan, D. Sivakumar. On Quasilinear Time Complexity Theory // Theoretical Computer Science. — Т. 148. — С. 325–349.
  8. Sedgewick, R. and Wayne K (2011). Algorithms, 4th Ed. p. 186. Pearson Education, Inc.
  9. Christos H. Papadimitriou. Computational complexity. — Reading, Mass.: Addison-Wesley, 1994. — ISBN 0-201-53082-1.
  10. Alan Cobham. Proc. Logic, Methodology, and Philosophy of Science II. — North Holland, 1965. — С. The intrinsic computational difficulty of functions.
  11. Martin Grötschel, László Lovász, Alexander Schrijver. Geometric Algorithms and Combinatorial Optimization. — Springer, 1988. — С. Complexity, Oracles, and Numerical Computation. — ISBN 0-387-13624-X.
  12. Alexander Schrijver. Combinatorial Optimization: Polyhedra and Efficiency. — Springer, 2003. — Т. 1. — С. Preliminaries on algorithms and Complexity. — ISBN 3-540-44389-4.
  13. Complexity Zoo Архивная копия от 26 июля 2010 на Wayback Machine Class QP: Quasipolynomial-Time
  14. 1 2 R. Impagliazzo, R. Paturi. On the complexity of k-SAT // Journal of Computer and System Sciences. — Elsevier, 2001. — Т. 62, вып. 2. — С. 367–375. — ISSN 1090-2724. — DOI:10.1006/jcss.2000.1727.
  15. Aaronson, Scott. A not-quite-exponential dilemma. — 5 April 2009.
  16. Complexity Zoo Архивная копия от 26 июля 2010 на Wayback Machine Class SUBEXP: Deterministic Subexponential-Time
  17. P. Moser. Baire’s Categories on Small Complexity Classes // Lecture Notes in Computer Science. — Berlin, New York: Springer-Verlag, 2003. — С. 333–342. — ISSN 0302-9743.
  18. P.B. Miltersen. DERANDOMIZING COMPLEXITY CLASSES // Handbook of Randomized Computing. — Kluwer Academic Pub, 2001. — С. 843.
  19. Greg Kuperberg. A Subexponential-Time Quantum Algorithm for the Dihedral Hidden Subgroup Problem // SIAM Journal on Computing. — Philadelphia: Society for Industrial and Applied Mathematics, 2005. — Т. 35, вып. 1. — С. 188. — ISSN 1095-7111. — DOI:10.1137/s0097539703436345.
  20. Oded Regev. A Subexponential Time Algorithm for the Dihedral Hidden Subgroup Problem with Polynomial Space. — 2004.
  21. Jörg Flum, Martin Grohe. Parameterized Complexity Theory. — Springer, 2006. — С. 417. — ISBN 978-3-540-29952-3.
  22. R. Impagliazzo, R. Paturi, F. Zane. Which problems have strongly exponential complexity? // Journal of Computer and System Sciences. — 2001. — Т. 63, вып. 4. — С. 512–530. — DOI:10.1006/jcss.2001.1774.
  23. Mayr,E. & Mayer,A. The Complexity of the Word Problem for Commutative Semi-groups and
    Polynomial Ideals // Adv. in Math.. — 1982. — Вып. 46. — С. 305-329.
  24. J.H. Davenport, J. Heintz. Real Quantifier Elimination is Doubly Exponential // J. Symbolic Comp.. — 1988. — Вып. 5. — С. 29-35..
  25. G.E. Collins. Proc. 2nd. GI Conference Automata Theory & Formal Languages. — Springer. — Т. 33. — С. 134-183. — (Lecture Notes in Computer Science).

Понравилась статья? Поделить с друзьями:
  • Возможные варианты финансирования бизнес идей
  • Время работы мфц в ярославле заволжский район
  • Времени работы программы tmax n соответствует
  • Возражение мы уже работаем с другой компанией
  • Временная прописка через управляющую компанию