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


100

Що таке псевдополіномічний час ? Чим він відрізняється від многочлена? Деякі алгоритми, що працюють у псевдополіномічний час, мають тривалість виконання, як O (nW) (для задачі 0/1 Knapsack ) або O (√n) (для пробного поділу ); чому це не вважається полиномним часом?


Повідомлення, пов’язані з цим - Складність алгоритму рекурсивного
RBT

Відповіді:


253

Щоб зрозуміти різницю між полиномним часом і псевдополіномічним часом, нам потрібно почати з формалізації того, що означає "поліноміальний час".

Загальна інтуїція для поліноміального часу - це "час 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 в 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 ). Причиною того, що папір "PRIMES є в P" була настільки вагомою, що її час виконання був (приблизно) O (log 12 n), що в залежності від кількості бітів дорівнює O (x 12 ).

То чому це має значення? Ну, у нас є багато алгоритмів псевдополіноміального часу для розбиття цілих чисел. Однак ці алгоритми, технічно кажучи, є алгоритмами експоненціального часу. Це дуже корисно для криптографії: якщо ви хочете використовувати шифрування RSA, вам потрібно мати можливість довіряти, що ми не можемо легко визначити числа. Збільшуючи кількість бітів у числах до величезного значення (скажімо, 1024 біт), ви можете зробити кількість часу, який повинен зайняти алгоритм факторингу псевдополіномічного часу, настільки великий, що це було б цілком і нездійсненно чинним числа. Якщо, з іншого боку, ми можемо знайти алгоритм факторингу багаточленного часу, це не обов'язково. Додавання більшої кількості бітів може призвести до того, що робота зростатиме на багато, але зростання буде лише поліноміальним, а не експоненціальним.

Однак, у багатьох випадках алгоритми псевдополіномічного часу є ідеальними, оскільки розмір чисел не буде надто великим. Наприклад, сортування підрахунку має час виконання O (n + U), де U - найбільше число в масиві. Це псевдополіномічний час (тому що для числового значення U потрібні біти O (log U), щоб час списання було експоненціальним). Якщо ми штучно обмежуємо U так, що U не надто велике (скажімо, якщо дозволити U 2), то час виконання - O (n), що насправді є полиномним часом. Ось так працює radix сортування : обробляючи числа по одному біту, час виконання кожного раунду дорівнює O (n), тому загальний час виконання - O (n log U). Це насправді є поліноміальний час, тому що при написанні n чисел для сортування використовуються біти Ω (n), а значення журналу U прямо пропорційно кількості бітів, необхідних для виписання максимального значення в масиві.

Сподіваюся, це допомагає!



4
Чому isPrimeскладність 's оцінюється як 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

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), який не є многочленом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.