Что такое псевдополиномиальное время? Чем оно отличается от полиномиального времени?

101

Что такое псевдополиномиальное время ? Чем оно отличается от полиномиального времени? Некоторые алгоритмы, работающие за псевдополиномиальное время, имеют время выполнения, например O (nW) (для задачи о ранце 0/1 ) или O (√n) (для пробного деления ); почему это не считается полиномиальным временем?

templatetypedef
источник

Ответы:

254

Чтобы понять разницу между полиномиальным и псевдополиномиальным временем, нам нужно начать с формализации того, что означает «полиномиальное время».

Обычная интуиция для полиномиального времени - это «время O (n k ) для некоторого k». Например, сортировка выбора выполняется за время O (n 2 ), которое является полиномиальным временем, в то время как решение TSP методом грубой силы занимает время O (n · n!), Что не является полиномиальным временем.

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

Размер входных данных проблемы - это количество битов, необходимых для записи этих входных данных.

Например, если входными данными для алгоритма сортировки является массив 32-битных целых чисел, то размер входных данных будет 32n, где n - количество записей в массиве. В графе с n узлами и m ребрами вход может быть определен как список всех узлов, за которым следует список всех ребер, для чего потребуется Ω (n + m) бит.

Учитывая это определение, формальное определение полиномиального времени следующее:

Алгоритм выполняется за полиномиальное время, если время его выполнения составляет O (x k ) для некоторой константы k, где x обозначает количество битов входных данных, переданных алгоритму.

При работе с алгоритмами, обрабатывающими графы, списки, деревья и т. Д., Это определение более или менее согласуется с обычным определением. Например, предположим, что у вас есть алгоритм сортировки, который сортирует массивы 32-битных целых чисел. Если для этого вы используете что-то вроде сортировки по выбору, время выполнения в зависимости от количества входных элементов в массиве будет O (n 2 ). Но как n, количество элементов во входном массиве, соответствует количеству битов ввода? Как упоминалось ранее, количество битов ввода будет x = 32n. Следовательно, если мы выразим время выполнения алгоритма через x, а не n, мы получим, что время выполнения равно O (x 2 ), и поэтому алгоритм работает за полиномиальное время.

Точно так же предположим, что вы выполняете поиск в глубину на графе, который занимает время O (m + n), где m - количество ребер в графе, а n - количество узлов. Как это соотносится с количеством введенных битов? Что ж, если мы предположим, что вход задан как список смежности (список всех узлов и ребер), то, как упоминалось ранее, количество входных битов будет x = Ω (m + n). Следовательно, время выполнения будет O (x), поэтому алгоритм работает за полиномиальное время.

Однако все начинает ломаться, когда мы начинаем говорить об алгоритмах, работающих с числами. Давайте рассмотрим проблему проверки того, является ли число простым или нет. Учитывая число n, вы можете проверить, является ли n простым, используя следующий алгоритм:

function isPrime(n):
    for i from 2 to n - 1:
        if (n mod i) = 0, return false
    return true

Так какова временная сложность этого кода? Что ж, этот внутренний цикл выполняется O (n) раз и каждый раз выполняет некоторую работу по вычислению n mod i (как действительно консервативная верхняя граница, это, безусловно, может быть выполнено за время O (n 3 )). Следовательно, этот общий алгоритм работает за время O (n 4 ) и, возможно, намного быстрее.

В 2004 году трое ученых-информатиков опубликовали статью под названием PRIMES is in P, в которой приводится алгоритм с полиномиальным временем для проверки того, является ли число простым. Это считалось знаковым результатом. Так в чем же дело? Разве у нас еще нет для этого полиномиального алгоритма, а именно приведенного выше?

К сожалению, нет. Помните, что формальное определение временной сложности говорит о сложности алгоритма как функции количества битов ввода. Наш алгоритм работает за время O (n 4 ), но как это зависит от количества входных битов? Что ж, запись числа n занимает O (log n) бит. Следовательно, если мы позволим x быть количеством битов, необходимых для записи ввода n, время выполнения этого алгоритма фактически будет O (2 4x ), что не является полиномом от x.

В этом суть различия между полиномиальным и псевдополиномиальным временем. С одной стороны, наш алгоритм O (n 4 ), который выглядит как полином, но с другой стороны, согласно формальному определению полиномиального времени, это не полиномиальное время.

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

10001010101011

то, скажем T, для завершения потребуется некоторое время, в худшем случае . Если я теперь добавлю один бит в конец числа, например:

100010101010111

Время выполнения теперь будет (в худшем случае) 2T. Я могу удвоить объем работы, выполняемой алгоритмом, просто добавив еще один бит!

Алгоритм выполняется за псевдополиномиальное время, если время выполнения является некоторым полиномом по числовому значению входных данных , а не по количеству битов, необходимых для его представления. Наш алгоритм первичного тестирования - это алгоритм с псевдополиномиальным временем, поскольку он выполняется за время O (n 4 ), но это не алгоритм с полиномиальным временем, потому что в зависимости от количества битов x, необходимых для записи ввода, время выполнения равно O (2 4 раза ). Причина того, что статья «PRIMES is in P» была настолько важной, заключалась в том, что время ее выполнения было (примерно) O (log 12 n), что как функция количества битов составляет O (x 12 ).

Так почему это важно? Что ж, у нас есть много алгоритмов псевдополиномиального времени для факторизации целых чисел. Однако эти алгоритмы, технически говоря, являются алгоритмами экспоненциального времени. Это очень полезно для криптографии: если вы хотите использовать шифрование RSA, вы должны быть уверены, что мы не можем легко разложить числа на множители. Увеличивая количество бит в числах до огромного значения (скажем, 1024 битов), вы можете сделать количество времени, которое должен занять алгоритм факторинга псевдополиномиального времени, настолько большим, что было бы полностью и совершенно невозможно разложить множители на множители. числа. Если, с другой стороны, мы можем найти алгоритм факторизации за полиномиальное время, это не обязательно так. Добавление большего количества битов может привести к значительному увеличению объема работы, но рост будет только полиномиальным, а не экспоненциальным.

Тем не менее, во многих случаях алгоритмы псевдополиномиального времени вполне подходят, потому что размер чисел не будет слишком большим. Например, подсчетная сортировка имеет время выполнения O (n + U), где U - наибольшее число в массиве. Это псевдополиномиальное время (поскольку числовое значение U требует для записи O (log U) бит, поэтому время выполнения экспоненциально зависит от размера ввода). Если мы искусственно ограничим U, чтобы U не было слишком большим (скажем, если мы позволим U равным 2), то время выполнения будет O (n), что на самом деле является полиномиальным временем. Вот как работает поразрядная сортировка : обрабатывая числа по одному бит за раз, время выполнения каждого раунда составляет O (n), поэтому общее время выполнения составляет O (n log U). Это на самом деле является полиномиальное время, поскольку для записи n чисел для сортировки используется Ω (n) бит, а значение log U прямо пропорционально количеству битов, необходимых для записи максимального значения в массиве.

Надеюсь это поможет!

templatetypedef
источник
27
Это должно быть объяснение из Википедии ...
Сандро Мейер
4
Почему isPrimeсложность оценивается как O (n ^ 4), а не просто O (n)? Я не понимаю. Если сложность n mod iне O (n ^ 3) .... что, конечно, не так.
fons
4
@Nobody Обычно мы думаем о стоимости модификации двух чисел как O (1), но когда вы имеете дело с произвольно большими числами, стоимость умножения увеличивается в зависимости от размера самих чисел. Чтобы быть предельно консервативным, я заявил, что вы можете вычислить моддинг по числу, меньшему n, как O (n ^ 3), что является большим перерасчетом, но все же не так уж плохо.
templatetypedef
1
@AndrewFlemming Это зависит от того, как число представлено в памяти. Я предполагал, что мы используем стандартное двоичное представление, где нам понадобится log_2 n бит для представления числа. Вы правы в том, что изменение базового представления изменит время выполнения в зависимости от размера ввода.
templatetypedef
1
Выбор O (n ^ 3) для n mod iслишком консервативен. Время modзависит от количества битов n, а не от nсамого себя, поэтому оно должно быть O ((log n) ^ 3).
dasblinkenlight 05
2

Псевдополиномиальная временная сложность означает полиномиальную величину / величину входных данных, но экспоненциальную по размеру входных данных.

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

Из псевдокода рюкзака мы можем найти временную сложность O (nW).

// Input:
// Values (stored in array v) 
// Weights (stored in array w)
// Number of distinct items (n) //
Knapsack capacity (W) 
for w from 0 to W 
    do   m[0, w] := 0 
end for  
for i from 1 to n do  
        for j from 0 to W do
               if j >= w[i] then 
                      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i]) 
              else 
                      m[i, j] := m[i-1, j]
              end if
       end for 
end for

Здесь W не является полиномиальным по длине ввода, что делает его псевдополиномиальным.

Пусть s будет количеством битов, необходимых для представления W

i.e. size of input= s =log(W) (log= log base 2)
-> 2^(s)=2^(log(W))
-> 2^(s)=W  (because  2^(log(x)) = x)

Теперь running time of knapsack= O (nW) = O (n * 2 ^ s), что не является полиномиальным.

Ади Агарвал
источник