Русская Википедия:Временная сложность алгоритма
В информатике временна́я сложность алгоритма определяется как функция от длины строки, представляющей входные данные, равная времени работы алгоритма на данном входе[1]. Временная сложность алгоритма обычно выражается с использованием нотации «O» большое, которая учитывает только слагаемое самого высокого порядка, а также не учитывает константные множители, то есть коэффициенты. Если сложность выражена таким способом, говорят об асимптотическом описании временной сложности, то есть при стремлении размера входа к бесконечности. Например, если существует число <math>n_0</math>, такое, что время работы алгоритма для всех входов длины <math>n>n_0</math> не превосходит <math>5n^3+3n</math>, то временную сложность данного алгоритма можно асимптотически оценить как <math>O(n^3)</math>.
Временная сложность обычно оценивается путём подсчёта числа элементарных операций, осуществляемых алгоритмом. Время исполнения одной такой операции при этом берётся константой, то есть асимптотически оценивается как <math>O(1)</math>. В таких обозначениях полное время исполнения и число элементарных операций, выполненных алгоритмом, отличаются максимум на постоянный множитель, который не учитывается в O-нотации. Так как время работы алгоритма может отличаться на входах одного и того же размера, обычно используется время работы в худшем случае, которое обозначается как <math>T(n)</math> и определяется как наибольшее по всем входным данным длины <math>n</math> время работы алгоритма. Реже, и это обычно оговаривается специально, вычисляется Шаблон:Не переведено 5, то есть математическое ожидание времени работы по всем возможным входам. Время работы алгоритма может быть классифицировано в зависимости от того, какая функция находится под О-нотацией. Например, алгоритм с <math>T(n) \in O(n)</math> называют алгоритмом с линейным временем работы, а алгоритм с <math>T(n)\in O(n^\alpha)</math> для некоторого <math>\alpha > 1</math> называют полиномиальным.
Таблица сложностей по времени
Шаблон:Further Следующая таблица обобщает наиболее часто встречающиеся классы сложности. В таблице <math>poly(x)</math> обозначает произвольный многочлен от <math>x</math>, то есть <math>poly(x)=x^{O(1)}</math>.
Название | Класс сложности | Время работы, <math>T(n)</math> | Примеры времени работы | Примеры алгоритмов |
---|---|---|---|---|
константное время | <math>O(1)</math> | <math>10</math> | Определение чётности целого числа (представленного в двоичном виде) | |
обратная функция Аккермана | <math>O(\alpha(n))</math> | Амортизационный анализ на одну операцию с использованием непересекающихся множеств | ||
итерированно логарифмическое время | <math>O(\log^*n)</math> | Распределённые раскраски циклов | ||
дважды логарифмическое | <math>O(\log \log n)</math> | Время амортизации на одну операцию при использовании ограниченной Шаблон:Не переведено 5[2] | ||
логарифмическое время | Шаблон:Не переведено 5 | <math>O(\log n)</math> | <math>\log n</math>, <math>\log n^2</math> | Двоичный поиск |
полилогарифмическое время | <math>poly(\log n)</math> | <math>(\log n)^2</math> | ||
сублинейное время | <math>O(n^c)</math> при <math>0 < c < 1</math> | <math>n^{1/2}</math>, <math>n^{2/3}</math> | Поиск в k-мерном дереве | |
линейное время | <math>O(n)</math> | <math>n</math> | Поиск наименьшего или наибольшего элемента в неотсортированном массиве | |
«n log-звёздочка n» | <math>O(n\log^*n)</math> | Алгоритм триангуляции многоугольника Шаблон:Не переведено 5. | ||
линейно-логарифмическое время | <math>O(n \log n)</math> | <math>n \log n</math>, <math>\log n!</math> | Самая быстрая Шаблон:Не переведено 5 | |
квадратичное время | <math>O(n^2)</math> | <math>n^2</math> | Сортировка пузырьком, сортировка вставками, Шаблон:Не переведено 5 | |
кубическое время | <math>O(n^3)</math> | <math>n^3</math> | Обычное умножение двух <math>n \times n</math> матриц. Вычисление Шаблон:Не переведено 5. | |
полиномиальное время | P | <math>2^{O(\log n)} = poly(n)</math> | <math>n</math>, <math>n \log n</math>, <math>n^{10}</math> | Алгоритм Кармаркара для линейного программирования, АКС-тест простоты |
квазиполиномиальное время | QP | <math>2^{poly(\log n)}</math> | <math>n^{\log \log n}</math>, <math>n^{\log n}</math> | Самый быстрый известный <math>O(\log^2 n)</math> — аппроксимационный алгоритм для ориентированной задачи Штейнера. |
субэкспоненциальное время (первое определение) |
SUBEXP | <math>O(2^{n^\varepsilon})</math> для всех <math>\varepsilon > 0</math> | <math>2^{\log n^{\log \log n}}</math> | Если принять теоретические гипотезы, BPP содержится в SUBEXP.[3] |
субэкспоненциальное время (второе определение) |
<math>2^{o(n)}</math> | <math>2^{n^{1/3}}</math> | Самые быстрые известные алгоритмы разложения на множители целых чисел и проверки Шаблон:Не переведено 5 | |
экспоненциальное время (с линейной экспонентой) |
Шаблон:Не переведено 5 | <math>2^{O(n)}</math> | <math>1.1^n</math>, <math>10^n</math> | Решение задачи коммивояжёра с помощью динамического программирования |
экспоненциальное время | EXPTIME | <math>2^{poly(n)}</math> | <math>2^{n}</math>, <math>2^{n^2}</math> | Решение задачи о порядке перемножения матриц с помощью полного перебора |
факториальное время | <math>O(n!)</math> | <math>n!</math> | Решение задачи коммивояжёра полным перебором | |
дважды экспоненциальное время | Шаблон:Не переведено 5 | <math>2^{2^{poly(n)}}</math> | <math>2^{2^n}</math> | Проверка верности заданного утверждения в арифметике Пресбургера |
Алгоритм Евклида | <math>\sqrt{\log_{}(\min(a, b))}</math> | |||
<math>\sqrt[n]{\log_{n}(n)}</math> | 1 при n >= 1 | |||
Алгоритм Полига - Хеллмана | <math>O(\sum\limits_{i=1}^{s}\alpha_i(\log{p}+q_i))</math> | Если множители, на которые раскладывается <math>p-1</math>, достаточно маленькие, то алгоритм очень эффективен | ||
ρ-метод Полларда | <math display="inline">p^{1/2}</math> | Имеет эвристическую оценку сложность |
Константное время
Говорят, что алгоритм является алгоритмом константного времени (записывается как время <math>O(1)</math>), если значение <math>T(n)</math> ограничено значением, не зависящим от размера входа. Например, получение одного элемента в массиве занимает константное время, поскольку выполняется единственная команда для его обнаружения. Однако нахождение минимального значения в неотсортированном массиве не является операцией с константным временем, поскольку мы должны просмотреть каждый элемент массива. Таким образом, эта операция занимает линейное время, <math>O(n)</math>. Если число элементов известно заранее и не меняется, о таком алгоритме можно говорить как об алгоритме константного времени.
Несмотря на название «константное время», время работы не обязательно должно быть независимым от размеров задачи, но верхняя граница времени работы должна. Например, задача «обменять значения <math>a</math> и <math>b</math>, если необходимо, чтобы в результате получили <math>a \leq b</math>», считается задачей константного времени, хотя время работы алгоритма может зависеть от того, выполняется ли уже неравенство <math>a \leq b</math> или нет. Однако существует некая константа <math>t</math>, для которой время выполнения задачи всегда не превосходит <math>t</math>.
Ниже приведен пример кода, работающего за постоянное время:
int index = 5;
int item = list[index];
if (условие верно) then
выполнить некоторые операции с постоянным временем работы
else
выполнить некоторые операции с постоянным временем работы
for i = 1 to 100
for j = 1 to 200
выполнить некоторые операции с постоянным временем работы
Если <math>T(n)</math> равно <math>O(\alpha)</math>, где <math>\alpha</math> — константа, то это эквивалентно тому, что <math>T(n)</math> равно <math>O(1)</math>.
Логарифмическое время
Шаблон:Further Говорят, что алгоритм выполняется за логарифмическое время, если <math>T(n)=O(\log n)</math>. Поскольку в компьютерах принята двоичная система счисления, в качестве основания логарифма используется <math>2</math> (то есть <math>\log_2 n</math>). Однако при замене основания логарифма <math>\log_a n</math> и <math>\log_b n</math> отличаются лишь на постоянный множитель <math>\log_b a</math>, который в записи O-большое отбрасывается. Таким образом, <math>O(\log n)</math> является стандартной записью для алгоритмов логарифмического времени независимо от основания логарифма.
Алгоритмы, работающие за логарифмическое время, обычно встречаются при операциях с двоичными деревьями или при использовании двоичного поиска.
<math>O(\log n)</math> алгоритмы для работы с массивами данных размера <math>n</math> считаются высокоэффективными, поскольку отношение времени выполнения одной операции к размеру массива уменьшается с увеличением этого размера.
Очень простой пример такого алгоритма — поиск слов в словаре. Рассмотрим словарь <math>D</math>, в котором записаны <math>n</math> слов, отсортированных по алфавиту. При этом считаем, что все слова имеют длину <math>O(1)</math> и что мы можем за константное время получить доступ к любому элементу словаря по индексу. Пусть <math>D(k)</math> — это <math>k</math>-й элемент словаря, тогда можно проверить, лежит ли слово <math>\omega</math> в словаре за <math>O(\log n)</math>. Для этого нужно рассмотреть элемент словаря <math>D(\lfloor n/2 \rfloor)</math>, где <math>\lfloor~\rfloor</math> обозначает округление числа вниз. Если <math>D(\lfloor n/2 \rfloor) < \omega</math>, то нам стоит перейти в правую половину массива, иначе — в левую. В конце мы получим индекс первого слова, который равен или лексикографически больше, чем <math>\omega</math>.
int find(vector<string> &D, string w) {
int n = D.size();
int l = -1, r = n;
while(r - l > 1) {
int m = (l + r) / 2;
if(D[m] < w) {
l = m;
} else {
r = m;
}
}
return r;
}
Полилогарифмическое время
Говорят, что алгоритм работает за полилогарифмическое время, если <math>T(n)=O(\log^k n)</math> для некоторого <math>k</math>. Например, задача о порядке перемножения матриц может быть решена за такое время на Шаблон:Не переведено 5[4].
Сублинейное время
Говорят, что алгоритм выполняется за сублинейное время, если <math>T(n)=o(n)</math>. В частности, сюда включаются алгоритмы, чья временная сложность соответствует указанным выше, а также, например, поиск Гровера со сложностью <math>O(n^{1/2})</math>.
Обычно алгоритмы, которые, являясь точными, всё же работают за сублинейное время, используют распараллеливание процессов (как это делает алгоритм NC1 вычисления определителя матрицы), неклассические вычисления (как в поиске Гровера) или имеют гарантированное предположение о структуре входа (как алгоритмы двоичного поиска и многие алгоритмы обработки деревьев). Однако такие формальные языки как язык слов, имеющих единичный бит в позиции, определяемой первыми <math>\log n</math> битами слова, могут быть разрешимыми за сублинейное время даже несмотря на то, что любой бит может быть важен для определения принадлежности слова языку.
Термин алгоритм с сублинейным временем работы обычно используется для алгоритмов, которые, в отличие от приведённых выше примеров, работают на обычных последовательных моделях машин и не предполагают априорных знаний о структуре входа[5]. Однако для них допускается применение вероятностных методов и даже более того, зачастую они должны быть рандомизированными для любой нетривиальной задачи.
Так как такой алгоритм обязан давать ответ без полного чтения входных данных, он в очень сильной степени зависит от способов доступа, разрешённых во входном потоке. Обычно для потока, представляющего собой битовую строку <math>b_1,\dots,b_k</math>, предполагается, что алгоритм может за время <math>O(1)</math> запросить значение <math>b_i</math> для любого <math>i</math>.
Алгоритмы сублинейного времени, как правило, вероятностны и дают лишь аппроксимированное решение. Алгоритмы сублинейного времени выполнения возникают естественным образом при исследовании Шаблон:Не переведено 5.
Линейное время
Говорят, что алгоритм работает за линейное время, или за время <math>O(n)</math>, если его сложность равна <math>O(n)</math>. Неформально, это означает, что для достаточно большого размера входных данных время работы увеличивается линейно от размера входа. Например, процедура, суммирующая все элементы списка, требует время, пропорциональное длине списка. Это описание не вполне точно, поскольку время работы может существенно отличаться от точной пропорциональности, особенно для малых значений <math>n</math>.
Линейное время часто рассматривается как желательный атрибут алгоритма[6]. Было проведено много исследований для создания алгоритмов с (почти) линейным или лучшим временем работы. Эти исследования включали как программные, так и аппаратные подходы. В случае аппаратного исполнения некоторые алгоритмы, которые, с математической точки зрения, никогда не могут достичь линейного времени исполнения в стандартных моделях вычислений, могут работать за линейное время. Существуют некоторые аппаратные технологии, которые используют параллельность для достижения такой цели. Примером служит ассоциативная память. Эта концепция линейного времени используется в задачах сопоставления с образцом, таких как алгоритм Бойера — Мура и алгоритм Укконена.
Квазилинейное время
Говорят, что алгоритм работает за квазилинейное время, если <math>T(n)=O(n \log^k n)</math> для некоторой константы <math>k</math>. При <math>k=1</math> говорят о линейно-логарифмическом времени[7]. В терминах soft-O такое время работы записывается как <math>\tilde O(n)</math>. Для алгоритмов с квазилинейным временем работы также верна оценка <math>T(n) \in o(n^{1+\varepsilon})</math> для любого <math>\varepsilon > 0</math>. Таким образом, эти алгоритмы работают быстрее любого полинома от <math>n</math>, степень которого больше <math>1</math>.
Алгоритмы, работающие за квазилинейное время, вдобавок к линейно-логарифмическим алгоритмам, упомянутым выше, включают:
- Сортировка слиянием на месте: <math>O(n \log^2 n)</math>;
- Быстрая сортировка: математическое ожидание времени работы вероятностной версии алгоритма лежит в <math>O(n \log n)</math> на любом входе, для детерминированной же версии эта оценка верна только в среднем по всем входам;
- Сортировка кучей, сортировка слиянием, introsort, сортировка с помощью бинарного дерева, плавная сортировка, Шаблон:Не переведено 5, и другие: <math>O(n \log n)</math> в худшем случае;
- Быстрое преобразование Фурье: <math>O(n \log n)</math>;
- Алгоритмы на матрицах Монжа: <math>O(n \log n)</math>.
Линейно-логарифмическое время
Линейно-логарифмическое является частным случаем квазилинейного времени с показателем <math>k=1</math> на логарифмическом множителе.
Линейно-логарифмическая функция — это функция вида <math>n \log n</math> (то есть произведение линейного и логарифмического членов). Говорят, что алгоритм работает за линейно-логарифмическое время, если <math>T(n) = O(n \log n)</math>[8]. Таким образом, линейно-логарифмическая функция растёт быстрее, чем линейная, но медленнее, чем любой многочлен от <math>n</math> со степенью, большей <math>1</math>.
Во многих случаях время работы <math>n \log n</math> является просто результатом выполнения <math>n</math> раз операции со временем работы <math>O(\log n)</math>. Например, сортировка с помощью двоичного дерева создаёт двоичное дерево путём вставки в него каждого элемента массива размера <math>n</math> один за другим. Поскольку операция вставки в Шаблон:Не переведено 5 занимает время <math>O(\log n)</math>, общее время выполнения алгоритма будет линейно-логарифмическим.
Любая Шаблон:Не переведено 5 требует по меньшей мере линейно-логарифмического числа сравнений для наихудшего случая. Одно из простых обоснований этого факта с теоретико-информационной точки зрения выглядит таким образом: в результате сортировки мы получим перестановку длины <math>n</math>, однозначно определяющую порядок элементов. Всего есть <math>n!</math> различных сортировок, если мы захотим однозначно закодировать каждую из них некоторой последовательностью бит, нам потребуется в среднем не меньше <math>\log n! \sim n \log n</math> битов информации на одну перестановку. При этом результатом попарного сравнения двух элементов массива является ровно один бит информации — либо первый элемент меньше второго, либо нет. Таким образом, если мы используем только попарные сравнения для сортировки, отсортировать массив за время лучшее, чем <math>O(n \log n)</math> в худшем случае невозможно. В то же время такая оценка на многие рекурсивные сортировки, такие как сортировка слиянием, зачастую возникает из рекуррентного уравнения <math>T(n)=T(n/2)+O(n)</math>.
Субквадратичное время
Говорят, что алгоритм выполняется за субквадратичное время, если <math>T(n)=o(n^2)</math>.
Например, простые алгоритмы сортировки, основанные на сравнениях (такие как сортировка вставками), квадратичны. В то же время можно найти более продвинутые алгоритмы, которые имеют субквадратичное время выполнения (например, сортировка Шелла). Никакие сортировки общего вида не работают за линейное время, но переход от квадратичного к субквадратичному времени имеет большую практическую важность.
Полиномиальное время
Говорят, что алгоритм работает за полиномиальное время, если время работы ограничено сверху многочленом от размера входа алгоритма, то есть <math>T(n)=O(n^k)</math> для некоторой константы <math>k</math>[1][9]. Задачи, для которых алгоритмы с детерминированным полиномиальным временем существуют, составляют класс сложности P, который является центральным в теории вычислительной сложности. Шаблон:Не переведено 5 утверждает, что полиномиальное время является синонимом понятий «легко поддающийся обработке», «выполнимый», «эффективный» или «быстрый»[10].
Некоторые примеры полиномиальных алгоритмов:
- Алгоритм быстрой сортировки <math>n</math> целых чисел делает максимум <math>An^2</math> операций для некоторой константы <math>A</math>. Таким образом, он работает за <math>O(n^2)</math> и является полиномиальным алгоритмом.
- Все базовые арифметические операции (сложение, вычитание, умножение, деление и сравнение) могут быть выполнены за полиномиальное время.
- Максимальное паросочетание в графах может быть найдено за полиномиальное время.
Строго и слабо полиномиальное время
В некоторых контекстах, особенно в оптимизации, различают алгоритмы со строгим полиномиальным временем и слабо полиномиальным временем. Эти две концепции относятся только ко входным данным, состоящим из целых чисел.
Строго полиномиальное время определяется в арифметической модели вычислений. В этой модели базовые арифметические операции (сложение, вычитание, умножение, деление и сравнение) берутся за единицы выполнения, независимо от длины операндов. Алгоритм работает в строго полиномиальное время, если[11]
- число операций в арифметической модели вычислений ограничено многочленом от числа целых во входном потоке, и
- память, используемая алгоритмом, ограничена многочленом от размеров входа.
Любой алгоритм с этими двумя свойствами можно привести к алгоритму полиномиального времени путём замены арифметических операций на соответствующие алгоритмы выполнения арифметических операций на машине Тьюринга. Если второе из вышеприведённых требований не выполняется, это больше не будет верно. Если задано целое число <math>2^n</math> (которое занимает память, пропорциональную n в машине Тьюринга), можно вычислить <math>2^{2^n}</math> с помощью n операций, используя повторное возведение в степень. Однако память, используемая для представления <math>2^{2^n}</math>, пропорциональна <math>2^n</math>, и она скорее экспоненциально, чем полиномиально, зависит от памяти, используемой для входа. Отсюда — невозможно выполнить эти вычисления за полиномиальное время на машине Тьюринга, но можно выполнить за полиномиальное число арифметических операций.
Обратно — существуют алгоритмы, которые работают за число шагов машины Тьюринга, ограниченных полиномиальной длиной бинарно закодированного входа, но не работают за число арифметических операций, ограниченное многочленом от количества чисел на входе. Алгоритм Евклида для вычисления наибольшего общего делителя двух целых чисел является одним из примеров. Для двух целых чисел <math>a</math> и <math>b</math> время работы алгоритма ограничено <math>O((\log\ a + \log\ b)^2)</math> шагам машины Тьюринга. Это число является многочленом от размера бинарного представления чисел <math>a</math> и <math>b</math>, что грубо можно представить как <math>\log\ a + \log\ b</math>. В то же самое время число арифметических операций нельзя ограничить числом целых во входе (что в данном случае является константой — имеется только два числа во входе). Ввиду этого замечания алгоритм не работает в строго полиномиальное время. Реальное время работы алгоритма зависит от величин <math>a</math> и <math>b</math>, а не только от числа целых чисел во входе.
Если алгоритм работает за полиномиальное время, но не за строго полиномиальное время, говорят, что он работает за слабо полиномиальное время[12]. Хорошо известным примером задачи, для которой известен слабо полиномиальный алгоритм, но не известен строго полиномиальный алгоритм, является линейное программирование. Слабо полиномиальное время не следует путать с псевдополиномиальным временем.
Классы сложности
Концепция полиномиального времени приводит к нескольким классам сложности в теории сложности вычислений. Некоторые важные классы, определяемые с помощью полиномиального времени, приведены ниже.
- P: Класс сложности задач разрешимости, которые могут быть решены в детерминированной машине Тьюринга за полиномиальное время.
- NP: Класс сложности задач разрешимости, которые могут быть решены в недетерминированной машине Тьюринга за полиномиальное время.
- ZPP: Класс сложности задач разрешимости, которые могут быть решены с нулевой ошибкой в вероятностной машине Тьюринга за полиномиальное время.
- RP: Класс сложности задач разрешимости, которые могут быть решены с односторонними ошибками в вероятностной машине Тьюринга за полиномиальное время.
- BPP: Класс сложности задач разрешимости, которые могут быть решены с двусторонними ошибками в вероятностной машине Тьюринга за полиномиальное время.
- BQP: Класс сложности задач разрешимости, которые могут быть решены с двусторонними ошибками в квантовой машине Тьюринга за полиномиальное время.
P является наименьшим классом временной сложности на детерминированной машине, которая является Шаблон:Не переведено 5 в терминах изменения модели машины. (Например, переход от одноленточной машины Тьюринга к мультиленточной может привести к квадратичному ускорению, но любой алгоритм, работающий за полиномиальное время на одной модели, будет работать за полиномиальное время на другой.)
Суперполиномиальное время
Говорят, что алгоритм работает за суперполиномиальное время, если T(n) не ограничен сверху полиномом. Это время равно ω(nc) для всех констант c, где n — входной параметр, обычно — число бит входа.
Например, алгоритм, осуществляющий 2n шагов, для входа размера n требует суперполиномиального времени (конкретнее, экспоненциального времени).
Ясно, что алгоритм, использующий экспоненциальные ресурсы, суперполиномиален, но некоторые алгоритмы очень слабо суперполиномиальны. Например, тест простоты Адлемана — Померанса — Румели работает за время nO(log log n) на n-битном входе. Это растёт быстрее, чем любой полином, для достаточно большого n, но размер входа должен стать очень большим, чтобы он не доминировался полиномом малой степени.
Алгоритм, требующий суперполиномиального времени, лежит вне класса сложности P. Шаблон:Не переведено 5 утверждает, что эти алгоритмы непрактичны, и во многих случаях это так. Поскольку задача равенства классов P и NP не решена, никаких алгоритмов для решения NP-полных задач за полиномиальное время в настоящее время не известно.
Квазиполиномиальное время
Алгоритмы квазиполиномиального времени — это алгоритмы, работающие медленнее, чем за полиномиальное время, но не столь медленно, как алгоритмы экспоненциального времени. Время работы в худшем случае для квазиполиномиального алгоритма равно <math>2^{O((\log n)^c)}</math> для некоторого фиксированного c. Хорошо известный классический алгоритм разложения целого числа на множители, общий метод решета числового поля, который работает за время около <math>2^{\tilde{O}(n^{1/3})}</math>, не является квазиполиномиальным, поскольку время работы нельзя представить как <math>2^{O((\log n)^c)}</math> для некоторого фиксированного c. Если константа «c» в определении алгоритма квазиполиномиального времени равна 1, мы получаем алгоритм полиномиального времени, а если она меньше 1, мы получаем алгоритм сублинейного времени.
Алгоритмы квазиполиномиального времени обычно возникают при сведении NP-трудной задачи к другой задаче. Например, можно взять NP-трудную задачу, скажем, 3SAT, и свести её к другой задаче B, но размер задачи станет равным <math>2^{O((\log n)^c)}</math>. В этом случае сведение не доказывает, что задача B NP-трудна, такое сведение лишь показывает, что не существует полиномиального алгоритма для B, если только не существует квазиполиномиального алгоритма для 3SAT (а тогда и для всех NP-задач). Подобным образом — существуют некоторые задачи, для которых мы знаем алгоритмы с квазиполиномиальным временем, но для которых алгоритмы с полиномиальным временем неизвестны. Такие задачи появляются в аппроксимационых алгоритмах. Знаменитый пример — ориентированная задача Штайнера, для которой существует аппроксимационный квазиполиномиальный алгоритм с аппроксимационным коэффициентом <math>O(\log^3 n)</math> (где n — число вершин), но существование алгоритма с полиномиальным временем является открытой проблемой.
Класс сложности QP состоит из всех задач, имеющих алгоритмы квазиполиномиального времени. Его можно определить в терминах Шаблон:Не переведено 5 следующим образом[13]:
- <math>\mbox{QP} = \bigcup_{c \in \mathbb{N}} \mbox{DTIME}(2^{(\log n)^c})</math>
Связь с NP-полными задачами
В теории сложности нерешённая проблема равенства классов P и NP спрашивает, не имеют ли все задачи из класса NP алгоритмы решения за полиномиальное время. Все хорошо известные алгоритмы для NP-полных задач, наподобие 3SAT, имеют экспоненциальное время. Более того, существует гипотеза, что для многих естественных NP-полных задач не существует алгоритмов с субэкспоненциальным временем выполнения. Здесь «субэкспоненциальное время» взято в смысле второго определения, приведённого ниже. (С другой стороны, многие задачи из теории графов, представленные естественным путём матрицами смежности, разрешимы за субэкспоненциальное время просто потому, что размер входа равен квадрату числа вершин.) Эта гипотеза (для задачи k-SAT) известна как Шаблон:Не переведено 5[14]. Поскольку она предполагает, что NP-полные задачи не имеют алгоритмов квазиполиномиального времени, некоторые результаты неаппроксимируемости в области аппроксимационных алгоритмов исходят из того, что NP-полные задачи не имеют алгоритмов квазиполиномиального времени. Например, смотрите известные результаты по неаппроксимируемости задачи о покрытии множества.
Шаблон:AnchorСубэкспоненциальное время
Термин Шаблон:Не переведено 5 время используется, чтобы выразить, что время выполнения некоторого алгоритма может расти быстрее любого полиномиального, но остаётся существенно меньше, чем экспоненциальное. В этом смысле задачи, имеющие алгоритмы субэкспоненциального времени, являются более податливыми, чем алгоритмы только с экспоненциальным временем. Точное определение «субэкспоненциальный» пока не является общепринятым[15], и мы приводим ниже два наиболее распространённых определения.
Первое определение
Говорят, что задача решается за субэкспоненциальное время, если она решается алгоритмом, логарифм времени работы которого растёт меньше, чем любой заданный многочлен. Более точно — задача имеет субэкспоненциальное время, если для любого ε > 0 существует алгоритм, который решает задачу за время O(2nε). Множество все таких задач составляет класс сложности SUBEXP, который в терминах DTIME можно выразить как[3][16][17][18].
- <math>\text{SUBEXP}=\bigcap_{\varepsilon>0} \text{DTIME}\left(2^{n^\varepsilon}\right)</math>
Заметим, что здесь ε не является частью входных данных и для каждого ε может существовать свой собственный алгоритм решения задачи.
Второе определение
Некоторые авторы определяют субэкспоненциальное время как время работы 2o(n)[14][19][20]. Это определение допускает большее время работы, чем первое определение. Примером такого алгоритма субэкспоненциального времени служит хорошо известный классический алгоритм разложения целых чисел на множители, общий метод решета числового поля, который работает за время около <math>2^{\tilde{O}(n^{1/3})}</math>, где длина входа равна n. Другим примером служит хорошо известный алгоритм для Шаблон:Не переведено 5, время работы которого равно <math>2^{O(\sqrt{n \log n})}</math>.
Заметим, что есть разница, является ли алгоритм субэкспоненциальным по числу вершин или числу рёбер. В Шаблон:Не переведено 5 эта разница присутствует явно путём указания пары <math>(L,k)</math>, задачи разрешимости и параметра k. SUBEPT является классом всех параметризованных задач, которые работают за субэкспоненциальное время по k и за полиномиальное по n[21]:
- <math>\text{SUBEPT}=\text{DTIME}\left(2^{o(k)} \cdot \text{poly}(n)\right).</math>
Точнее, SUBEPT является классом всех параметризованных задач <math>(L,k)</math>, для которых существует вычислимая функция <math>f : \mathbb N\to\mathbb N</math> с <math>f \in o(k)</math> и алгоритм, который решает L за время <math>2^{f(k)} \cdot \text{poly}(n)</math>.
Гипотеза об экспоненциальном времени
Гипотеза об экспоненциальном времени ('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.
- <math>\text{EXP} = \bigcup_{c \in \mathbb{N}} \text{DTIME}\left(2^{n^c}\right)</math>
Иногда термин экспоненциальное время используется для алгоритмов, для которых T(n) = 2O(n), где показатель является не более чем линейной функцией от n. Это приводит к классу сложности Шаблон:Не переведено 5.
- <math>\text{E} = \bigcup_{c \in \mathbb{N}} \text{DTIME}\left(2^{cn}\right)</math>
Двойное экспоненциальное время
Говорят, что алгоритм выполняется за Шаблон:Не переведено 5 время, если T(n) ограничено сверху значением 22poly(n), где poly(n) — некоторый многочлен от n. Такие алгоритмы принадлежат классу сложности Шаблон:Не переведено 5.
- <math>\mbox{2-EXPTIME} = \bigcup_{c \in \mathbb{N}} \mbox{DTIME}(2^{2^{n^c}})</math>
К хорошо известным дважды экспоненциальным алгоритмам принадлежат:
- Процедура вычисления для арифметики Пресбургера
- Вычисление базиса Грёбнера (в худшем случае[23])
- Элиминация кванторов в Шаблон:Не переведено 5 требует как минимум дважды экспоненциальное время выполнения[24] и может быть выполнена за это время[25].
См. также
Примечания
- ↑ 1,0 1,1 Шаблон:Книга
- ↑ Шаблон:Статья
- ↑ 3,0 3,1 Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Sedgewick, R. and Wayne K (2011). Algorithms, 4th Ed Шаблон:Wayback. p. 186. Pearson Education, Inc.
- ↑ Шаблон:Книга
- ↑ Шаблон:Книга
- ↑ Шаблон:Книга
- ↑ Шаблон:Книга
- ↑ Complexity Zoo Шаблон:Wayback Class QP: Quasipolynomial-Time Шаблон:Wayback
- ↑ 14,0 14,1 Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Complexity Zoo Шаблон:Wayback Class SUBEXP: Deterministic Subexponential-Time Шаблон:Wayback
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Книга
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья
- ↑ Шаблон:Статья.
- ↑ Шаблон:Книга