NP пълнота

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 20

ЮГОЗАПАДЕН УНИВЕРСИТЕТ „НЕОФИТ РИЛСКИ”

ПРИРОДО-МАТЕМАТИЧЕСКИ ФАКУЛТЕТ
ГР. БЛАГОЕВГРАД

РЕФЕРАТ
По „Теория на алгоритмите“
На тема:
„NP-пълнота. Полиномиално време.“

Изготвил: Димитър Акмаджов Проверил: ........................


Фак. №18250141006 /проф. д-р П. Миланов/
Спец.: Информатика, магистър

Благоевград, 2019 г.
1. Изчислителна сложност на алгоритмите. Същност на теорията на
NP- пълните задачи
Когато разглеждаме даден компютърен алгоритъм се интересуваме
най-общо от три негови свойства:
 простота и елегантност
 коректност
 бързодействие
Докато първото от тях всеки може да оцени интуитивно, то за
последните две е необходим много по-задълбочен анализ. Изучавайки
теорията на NP-пълните задачи ще придобием знания и умения, които ще
ни позволяват лесно и точно да определяме ефективността на даден
алгоритъм, да сравняваме алгоритми, да повишаваме бързодействието им
чрез премерени и точни промени.
Теорията на NP- пълните задачи възниква в началото на 70-те години
на XX век във връзка с необходимостта от теоретична оценка на
изчислителната сложност на алгоритмите. Компютризацията на различни
области на обществената практика води до възникване на широк клас
задачи, които трябва да бъдат решени в условията на многоразмерност и
времекритичност. Стремежът да се разработят достатъчно общи и
ефективни алгоритми за решение на голям клас дискретни и комбинаторни
задачи се сблъсква с определени трудности при практическата им
реализация, свързани с голям разход на памет и машинно време. В същото
време общата теория на алгоритмите изучава потенциалната възможност
за алгоритмично решение на даден клас задачи, без да се отчитат
необходимите за решението ресурси. Но наличието на алгоритъм за
получаване на решение все още не значи, че решението може да бъде
получено на практика. Възниква необходимост от по-задълбочено
изследване на алгоритмите във връзка с практическата им реализуемост,
която довежда до създаването на теорията на NP – пълните задачи. Тя
1
предоставя теоретична база за сравнение на алгоритмите, за оценка на
тяхната ефективност и анализ на необходимите за решението на дадена
задача изчислителни ресурси.
Пълно изложение на теорията на NP - пълните задачи е дадено в
книгата на M. Garey и D. Jonson (Гэри М., Джонсон Д. Вычислителные
машины и труднорешаемые задачи. Москва, Мир 1982), която и досега
остава една от най-изчерпателните и достъпни книги по въпроса.
Нека разгледаме следния програмен фрагмент:
1) n = 100;
2) sum = 0;
3) for (int i=0; i<n; i++)
4) for (int j=0; j<n; j++)
5) sum++;
Интересува ни колко бързо ще работи горната програма, т.е. какви са
критериите по-които се определя бързината й? Това, което можем да
направим експериментално е да проверим за колко време ще се изпълни и
ще завърши работата си. За да изследваме по-общо нейното поведение е
възможно да я изпълним за други стойности на n - нека резултатите от
последното са обобщени в следната таблица:
n=10 0.000001 сек.
n=100 0.0001 сек.
n=1000 0.01 сек.
n=10000 1.071 сек.
n=100000 106.543 сек.
n=100000 10663.6 сек.
0
Лесно се забелязва от таблицата, че когато увеличаваме n 10 пъти,
времето, необходимо на програмата, се увеличава с фактор 100.

2
Нека изследваме по-задълбочено горния фрагмент. На редове 1) и 2)
има статична инициализация, тя отнема константно време, ще го означим с
a. За операциите i=0, проверката i<n, i++ отново е необходимо константно
време (всяка от тях представлява константен брой инструкции за
процесора), ще го означим съответно с b, c, d. На ред 4) се времето
необходимо за операциите j=0; j<n; j++ означаваме с e, f, g. Последно, нека
операцията на ред 5) изисква константно време h.
С така въведените означения не е трудно да се пресметне общото
време, необходимо за завършване на програмата, за произволна стойност
на n:
a + b + n.c + n.d + n.(e + n.f + n.g + n.h) =
= a + b + n.c + n.d + n.e + n.n.f + n.n.g + n.n.h =
= n2.(f + g + h) + n.(c + d + e) + a + b
Тъй като a, b, c, d, e, f, g, h са константи, нека означим:
i=f+g+h
j=c+d+e
k=a+b
Така алгоритъмът се изпълнява за време:
i.n2 + j.n + k
Константите i, j и k са важни за бързодействието на алгоритъма, но
не са определящи. На практика когато изследваме ефективността на даден
алгоритъм, ние не се интересуваме от тях. Тези константи зависят
предимно от машинното представяне на нашата програма, както и от
бързината на хардуера, на който тя се изпълнява.
Нещо повече, когато изследваме поведението на нашия алгоритъм
можем да игнорираме дори и едночлените j.n и k и да оставим единствено
този, в който n участва в най-висока степен. Целта при подобно
„опростяване“ е да оставим само най-значимата характеристика за даден
алгоритъм, функцията, от която в най-голяма степен зависи времето на

3
изпълнение - и това е тази, която нараства най-бързо, когато се увеличава
размерът на входните данни.
Нека разгледаме две функции (които показват времето за изпълнение
на два дадени алгоритъма А1 и A2, в зависимост от n):
f = 2.n2 и
g = 200.n.
Лесно се забелязва, че въпреки, че коефициентът на g е много по-
голям от този на f, когато n премине някаква фиксирана стойност (в случая
n>100), алгоритъмът A2 ще решава задачата по-бързо от А1. Нещо повече,
колкото повече се увеличава n, толкова повече отношението между
времето за изпълнение на двата алгоритъма се увеличава в полза на A2.
Асимптотично алгоритъмът A2 е по-бърз и неговата сложност е линейна,
докато тази на A1 е квадратична.
Тези наблюдения могат да бъдат формализирани чрез апарата на
теорията на NP-пълните задачи.

2. Класове на сложност NP и P
Класовете NP и P играят основна роля в количествената теория на
алгоритмите и дават възможност за оценка на тяхната ефективност и
практическа реализируемост.
Класът NP включва всички изброителни задачи, които могат да
бъдат решени чрез метода на изчерпващото търсене, а класът P
съдържа изброителните задачи с полиномиална временна сложност.
Част от задачите в NP служат като еталон на сложност и се наричат
универсални или NP-пълни. Към тях могат полиномиално да се сведат
всички задачи от класа NP.
Една NP-задача A се нарича NP-пълна, ако за всяка друга задача B от
класа NP следва, че BA.

4
Въпросът за съотношението между класовете NP и P има голямо
теоретично и практическо значение. Графично им представяне е дадено на
фиг. 1. Очевидно P  NP. Съвпадението на двата класа P = NP означава
елиминация на изчерпващото търсене и насочване на усилията към
разработка на ефективни полиномиални алгоритми (които в момента
липсват, но може би откриването им е въпрос на време). Ако Р NP
трябва да приемем наличието на „труднорешаеми” задачи, за които не
съществуват ефективни алгоритми. Ако дадена задача П  NP \ P, то тя е
„труднорешаема”. Американският математически институт Clay предлага
1 000 000$ награда за отговор на въпроса P = NP? [Стойчев САА].
По-прецизна картина за предполагаемото съотношение на класовете
на сложност е дадена на следната фигура:

Фиг.1. Класове на сложност.

P-задачи
Това са задачи, за които съществува решение с полиномиална
сложност.
NP-задачи
NP - полиномиално проверими задачи. Дадена задача принадлежи
към класа NP, ако е възможно с полиномиална сложност да се провери
дали даден кандидат за решение действително е решение на задачата.

5
NP-пълни задачи
Универсални или NP-пълни задачи, към които полиномиално се
свеждат всички задачи от класа NP.
еxp-time задачи
Това са задачи, които могат да се решат с експоненциална сложност.
Този клас съдържа предишните три, но не е всеобхватен — има задачи, за
които най-добрите алгоритми са със сложност по-висока от
експоненциалната.
Space задачи. Това са задачи, при които критична е паметта, която
използват (като функция от големината на входните данни). При тях не се
интересуваме от сложността на алгоритъма за решаването им. Този клас
може да се разпадне до няколко подкласа: P-space (задачи за които е
необходима полиномиална памет), exp-space (експоненциална памет) и др.
Ясно е, че изчислителната сложност на алгоритъма е винаги поне
толкова, колкото е сложността по памет.
Нерешими задачи
Съществуват алгоритмични задачи, за които може да се докаже, че
не могат да бъдат решени, независимо от това с колко време и памет
разполагаме.

3.Изчислителна сложност на алгоритмите


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

6
Очевидно, съществените ресурси, влияещи на практическата
реализируемост на алгоритмите и тяхната ефективност са времето за
работа на алгоритъма и необходимата памет за съхранение на данните.
При голям размер на задачата може да се окаже, че тя не може да
бъде решена за „разумно” време или изисква памет за съхранение на
данни, която не може да се предостави. Възниква необходимост от
предварителна оценка на изчислителната сложност (временна сложност,
сложност по памет) на алгоритмите в зависимост от размера на решаваните
задачи. Обикновено ефективността на алгоритмите се свързва с временната
им сложност, тъй като времето е доминиращ фактор при оценка на
възможността за практическата им реализуемост.
Временна сложност на алгоритмите. Временната сложност на
алгоритмите е количествена оценка на времето за работа на алгоритъма в
зависимост от размера на задачата. Ясно е, че времето за решение зависи
от избраната схема за кодиране и модела на изчислителното устройство.
Без ограничение на общността ги приемаме за фиксирани. Най-често за
модел на изчислително устройство при оценка на изчислителната
сложност на алгоритмите се приема машината на Тюринг или машина с
произволен достъп до паметта.
Временната сложност на алгоритмите се представя чрез
функция, която на всяка входна дължина n поставя в съответствие
максималното време за решение на индивидуалните задачи с тази
дължина.

max
T ( n) = T(I)
I, |a( I )|= n

За много алгоритми определянето на точния вид на T(n) е сложна


задача. Затова се изследва поведението на T(n), или т.н. асимптотична
сложност, при която се оценява скоростта на нарастване на временната

7
сложност при големи стойности на n (n). Използват се следните
класове асимптотична сложност:

T(n) = O(f(n)), ако  c>0 n0>0, такива че T(n)  cf(n) за  n n0. В този
случай казваме, че f(n) е горна асимптотична граница на T(n). Времето
за решение на дадена задача T(n) расте със скорост по-малка или равна на
скоростта на растеж на функцията f(n).

T(n) = (f(n)), ако  c>0 n0>0, такива че T(n)  cf(n) за  n n0. В този
случай казваме, че f(n) е долна асимптотична граница на T(n).
Временната сложност T(n) расте със скорост по-голяма или равна на
скоростта на растеж на функцията f(n).

T(n) = (f(n)), ако T(n) = O(f(n)) и T(n) = (f(n)). В този случай


функциите T(n) и f(n) растат с една и съща скорост.

Полиномиални и експоненциални алгоритми


В зависимост от оценката на временната им сложност, алгоритмите
се разделят на два класа: полиномиални и експоненциални.
Даден алгоритъм се нарича полиномиален (или с полиномиална
временна сложност), ако временната му сложност T(n) = O(p(n)), където
p(n) е полином от размера на задачата n. В противен случай алгоритъмът се
нарича експоненциален, или с експоненциална временна сложност.
Временната сложност на полиномиалните алгоритми се оценява с
O(n), O(n2), O(n3)… Този клас е доста обширен, за много от изброителните
задачи съществуват полиномиални методи за решение. От голямо значение
за практическата реализация е степента на полиномиалната функция. Нещо
повече, в редица алгоритми участват и функции с асимптотично
нарастване, което е по-ниско от това на линейната функция: такива са
логаритмичната функция, обратната функция на Акерман и др.

8
Временната сложност на експоненциалните алгоритми се оценява с
O(2n), O(3n), O(nn), O(n!) и т.н. NP е съкращение от non-deterministic
polynomial time, т.е. полиномиално проверими задачи. Дадена задача
принадлежи към класа NP, ако е възможно с полиномиална сложност да се
провери дали даден кандидат за решение действително е решение, без да
се интересуваме колко време ще отнеме намирането на този кандидат. Така
за NP-задачите е характерно, че ако веднъж успеем да "налучкаме"
правилното решение, то лесно можем да го проверим.
Описаният в достъпната литература практически опит в решението
на многоразмерни реални задачи показва, че двата класа силно се
различават по трудоемкост. Това различие се проявява особено ясно при
теоретична оценка на времето за решение на задачи с голяма рамерност.
Следващата таблица показва скоростта на нарастване на времето за
решение при различна временна сложност и входен размер.
Временна
сложност
Полиномиална Експоненциална
Размер
O(n2) O(n5) O(n10) O(2n) O(3n) O(n!) O(nn)

0,000001 0,001 1,667 0,00001 0,00059 0,036 1,67


10
сек сек мин сек сек сек мин

0,000004 0,032 1,185 0,01 35 7,71 3,33E+08


20
сек сек дни сек сек века века

0,000009 0,243 68 11 24 8,41E+14 6,53E+26


30
сек сек дни сек дни века века

0,000025 3,125 31 130 2,28E+06 9,64E+46 2,82E+67


50
сек сек год дни века века века

0,000100 1,667 317,098 4,02E+12 1,63E+30 2,96E+140 3,17E+182


100
сек мин века века века века века

0,002500 3,617 3,E+09 1,04E+133 1,15E+221


500
сек дни века века века ∞ ∞
Таблица 1. Оценка на времето за решение при различна временна
сложност на алгоритмите и различен входен размер на задачите.

9
От таблицата се вижда, че увеличението на входния размер на
задачата влияе по различен начин върху времето за решение при
полиномиалните и експоненциални алгоритми. Поради значително по-
бавното нарастване на времето при полиномиалните алгоритми, те се
приемат за ефективни, практически използваеми, „добри” алгоритми.
Бързото нарастване на времето при експоненциалните ги прави
практически неприложими при голям размер на задачите, т.е. „лоши”
алгоритми. Липсата на полиномиални алгоритми за решение на дадена
задача я прави алгоритмично нерешима в приложен смисъл.
Съществената разлика между полиномиалните и експоненциалните
алгоритми се състои в следното. Анализът на дискретните задачи показва,
че те могат да бъдат решени чрез изчерпващо търсене в множеството от
потенциални решения, което включва две стъпки:
1. Пораждане на решения без пропуски и повторения;
2. Проверка за допустимост.
Типични примери са задачата за търговския пътник, задачата за
раницата и много други, наричани изброителни, при които решението се
търси между краен в теоретично отношение брой варианти. На практика
обаче, броят на вариантите, а съответно и времето за решение, расте
експоненциално при увеличение на размера. При големи размерности
такива задачи не могат да бъдат решени за обозримо време, т.е. те са
практически неразрешими. В това се състои и основния проблем на
дискретната математика: съществува ли принципна възможност за
намиране на решение без изчерпване на експоненциален брой варианти,
т.е. елиминиране на изчерпващото търсене. На този въпрос досега не е
намерен задоволителен отговор.

10
Сводимост на задачите в класа Р.
Казваме, че задачата A е сводима към задачата B и пишем AB, ако е
възможно да се намери алгоритъм за решението на А, използващ
полиномиален брой обръщения към функция, решаваща B, и всички
изчисления извън тези обръщения са с полиномиална сложност.
Сводимостта формално означава следното: ако П 1, П2  NP, то П1 е
сводима към П2, ако методът за решение на П 2 може да бъде преобразуван
в метод за решение на П1. Сводимостта е полиномиална, ако такова
преобразуване може да се извърши за полиномиално време.
Тогава, ако една задача B е от класа P, и AB, то следва, че А също е в
класа P. Да илюстрираме казаното дотук с един пример: Нека имаме
задачата за намиране на Хамилтонов цикъл в граф - т.е. дали в графа
съществува цикъл, в който всеки връх участва точно по веднъж. Тази
задача може да се реши, като се използва следния пример:
for ( всяко ребро (i,j) от графа) {
if (съществува прост път с дължина n-1 върха от i до j) {
return yes; /* Съществува Хамилтонов цикъл – това е
намереният път + реброто (i,j)*/
}
}
return no;
Алгоритъмът извършва m обръщения към задачата за намиране на
най-дълъг път в граф и сложността извън тази задача е О(m). Прилагайки
дефиницията по-горе това означава, че “Хамилтонов цикъл” ”Най-дълъг
прост път”. Тъй като обаче не е известен полиномиален алгоритъм за
решението на “най-дълъг прост път”, то не можем да твърдим, че
Хамилтоновият цикъл е полиномиално решима задача.

4. NP-пълни задачи. Теорема на Кук.


Началото на развитие на теорията на NP-пълните задачи е поставено
от Кук (S. A Cook) през 1971 год. в работата му „Сложност на процедурите
за доказателство на теореми”.
11
Основната заслуга на Кук е доказателството, че класът на NP-
пълните задачи не е празен, т.е. съществува поне една NP-пълна задача,
към която са полиномиално сводими всички задачи от NP. Тази „първа”
универсална задача от булевата логика се нарича задача за изпълнимост и
се формулира (не строго) по следния начин:
Задача за изпълнимост(удовлетворимост):
Дадено е множество от булеви променливи U = {u 1, u2, … un}. Нека
D1, D2, …Dm са дизюнкции над U от вида: uк1  uк2  ....  uкр и C е
конюнкция на D1, D2, …Dm, C= D1  D2  … Dm. Пита се: съществува ли
такъв набор от стойности на променливите, за който C получава стойност
„истина”.
Фундаменталната теорема на Кук гласи следното:
Задачата за изпълнимост е NP-пълна задача. Задачата за
удовлетворимост на булева функция е в известен смисъл „първата“ NP-
пълна задача. Тя често се използва за доказателство на теоремата на Кук ,
като чрез свеждане към нея се доказва NP-пълнотата на много други
задачи. Ще разгледаме тази задача и ще я решим чрез търсене с връщане.
Нека са дадени булевите променливи X1,X2,...,Xn, като с X̄ 1 , X̄ 2,..., X̄ n
означаваме отрицанието на тези променливи, т.е. Xi е „истина” тогава и
само тогава, когато X̄ i е „лъжа”. Ще използваме стандартните операции за
конструиране на булеви изрази  и , означаващи съответно логическо „и“
и „или“.
1 1=1 1 1=1

1 0=0 1 0=1
0 1=0 0 1=1
0 0=0 0 0=0

Даден булев израз на променливите X1,...,Xn се нарича удовлетворим,


ако съществува набор от стойности „истина“ или „лъжа“ за X1,...,Xn, такъв

12
че стойността на израза да бъде „истина”. Задачата се състои в да се
провери дали даден израз е удовлетворим.
Например, изразът ( X̄ 2  X2  X1)  X̄ 3 е удовлетворим за X1=1, X2=0,
X3=0, или X1=1, X2=1, X3=0 (където с 1 сме означили, че променливата има
стойност „истина”, а с 0 - „лъжа”). Проверката, дали един израз е
удовлетворим, е NP-задача: ако имаме някакво присвояване на стойности
на променливите, можем (както ще се види след малко) с полиномиална
сложност да проверим, дали то е решение, но за самото намиране на това
присвояване не е известен полиномиален алгоритъм.
Преди да преминем към решението на задачата, ще припомним
някои елементи от математическата логика. Дизюнкт се нарича булев
израз, в който участва само операцията „или“. Булев израз в конюнктивна
нормална форма е израз, в който участват само дизюнкти, обединени с
операцията „и“. С многократно прилагане на тъждеството
A  (B  C) = (A  B) (A  C)
произволен булев израз може да се представи в конюнктивна
нормална форма. Ето пример за такъв израз:
(X1  X4)  ( X̄ 1  X2)  (X1  X̄ 3 )  ( X̄ 2  X3  X̄ 4)  ( X̄ 1  X̄ 2  X̄ 3 )

За да проверим дали този израз е удовлетворим, можем да приложим


следния комбинаторен подход - пробваме всичките 2 n на брой
присвоявания на променливите и за всяко проверяваме дали изразът е
удовлетворен. Така алгоритъмът за търсене с връщане ще добие следния
вид:
Нека инициализираме i=1.
1)Присвояваме на i-тата променлива стойност „истина” и
преминаваме към (i+1)-вата променлива (правим стъпка напред). Ако това
присвояване по-късно не доведе до решение, се връщаме (стъпка назад),

13
присвояваме на променливата стойност „лъжа” и отново опитваме да
продължим.
2)Граничен случай е налице, когато на всички променливи е
присвоена някаква стойност - тогава се изчислява стойността на целия
израз и се проверява дали тя е „истина”.
В реализацията на описания алгоритъм булевият израз (приведен в
конюнктивна нормална форма) ще представим с двумерен масив - всеки
дизюнкт се асоциира с един ред от масива. Булевите променливите от един
ред (дизюнкт) ще представяме с целите числа от 1 до n, а за отрицание на
променливата i ще използваме числото – i. В масива values[] се намират
стойностите (0 или 1), които сме присвоили на всяка една променлива.
Функцията true() проверява стойността на булевият израз въз основа на
направените във values[] присвоявания и връща 1, ако изразът е
удовлетворим. Тази проверка се извършва лесно: За да има целият израз
стойност „истина” трябва всеки един от дизюнктите да има стойност
„истина” (тъй като операцията между дизюнктите е „и“). За да има един
дизюнкт стойност „истина”, то поне една от променливите в него трябва да
бъде със стойност „истина” или отрицание на променлива да бъде със
стойност „лъжа” - това се дължи на факта, че всички променливи в един
дизюнкт са обединени с операцията „или“.
Присвояване на стойностите на променливите в алгоритъма по-горе,
извършва функцията assign(int i), като в граничния случай (i>n) се извиква
функцията true(), за да провери дали изразът е удовлетворен (т.е. да
провери дали присвояването е решение на задачата). Следва и пълната
реализация:
bool.cpp
#include <stdio.h>

const int N=4; // Брой на булевите променливи


const int K=5; // Брой на дизюнктите

const int expr[][K]={


{ 1, 4},

14
{-1, 2},
{ 1, -3},
{-2, 3,-4},
{-1,-2,-3}};

int values[N+1];

void printAssign(){
printf("Изразът е удовлетворим за: ");
for (int i=1; i<=N; i++) printf("X%d=%d ",i,values[i]); printf("\n");
}

//във всеки от K-те дизюнкта трябва поне една променлива да бъде със стойност
“истина”
int true() {
for (int i=0; i<K; i++) {
int j=0;
int ok=0;
while (expr[i][j]!=0){
int p=expr[i][j];
if ((p>0)&&(values[p]==1)) {ok=1; break; }
if ((p<0)&&(values[-p]==0)) {ok=1; break; }
j++;
}
if (!ok) return 0;
}
return 1;
}

// присвоява стойности на променливите


void assign(int i) {
if (i>N) {
if (true()) printAssign();
return;
}
values[i]=1;
assign(i+1);
values[i]=0;
assign(i+1);
}

void main() {
assign(1);
}

Самото присвояването в пример с 3 булеви променливи е следното:

Фиг. 2. С по-тъмно са отбелязани върховете, които водят до решение на


X1(X2X3)

15
При NP-пълните задачи (и в частност задачата за удовлетворимост),
от всички кандидати за решение по неявен начин се формира дърво (или
граф), което алгоритъмът за търсене с връщане обхожда. Често обаче е
излишно изследването на голяма част от това дърво. Например, за израза
X1(X2X3) при присвояване X1=0 веднага се забелязва, че няма да
достигнем до решение, независимо от стойностите, които ще получат
останалите две променливи X2 и X3. Когато променливите са повече и
булевият израз е по-сложен, подобно „изрязване“ на изследваното дърво
може да доведе до значително по-бърза програма.
Реализацията на това изрязване ще осъществим като извършим
следните модификации в горната програма:
1)На функцията true() ще добавим нов аргумент t означаващ, че са
присвоени стойности само на първите t променливи. Така, ако един
дизюнкт е съставен само от променливи с номера по-малки или равни на t
и нито една от тях няма стойност „истина”, то целия булев израз е „лъжа”
и няма смисъл да се присвояват стойности на останалите n-t променливи.
Ако в дизюнкта има променливи, на които още не е присвоена стойност, то
неговата стойност не може да се определи и продължаваме разглеждането.
2)При генерирането се извиква функцията true() в началото на всяка
стъпка. Така прекъсваме по-нататъшното генериране веднага, щом то стане
безсмислено - стойността на булевия израз ще бъде „лъжа”, независимо от
това как ще се извършат останалите присвоявания.

boolcut.cpp
boolcut.cpp
#include <stdio.h>

const int n=4; // Брой на булевите променливи


const int K=5; // Брой на дизюнктите

const int expr[][K]={


{ 1, 4},

16
{-1, 2},
{ 1, -3},
{-2, 3,-4},
{-1,-2,-3}};

int values[n+1];

void printAssign(){
printf("Изразът е удовлетворим за: ");
for (int i=1; i<=n; i++) printf("X%d=%d ",i,values[i]); printf("\n");
}

//във всеки от K-те дизюнкта трябва поне една променлива да бъде със
стойност “истина”
int true(int t) {
for (int i=0; i<K; i++) {
int j=0;
int ok=0;
while (expr[i][j]!=0){
int p=expr[i][j];
if ((p>t)||(-p>t)) {ok=1; break; }
if ((p>0)&&(values[p]==1)) {ok=1; break; }
if ((p<0)&&(values[-p]==0)) {ok=1; break; }
j++;
}
if (!ok) return 0;
}
return 1;
}

// присвоява стойности на променливите


void assign(int i) {
if (!true(i-1)) return;
if (i>n) { printAssign(); return; }

values[i]=1;
assign(i+1);
values[i]=0;
assign(i+1);
}

void main() {
assign(1);
}

Важни следствия от теоремата на Кук са:


1.Ако съществува полиномиален алгоритъм за решение на задачата
за изпълнимост, то всички задачи от NP са полиномиално разрешими.

17
2.Обратно, ако някоя задача от NP е „труднорешаема”, то такава е и
задачата за изпълнимост.
3.Всички въпроси, свързани с анализ на изчислителната сложност на
алгоритмите се свеждат до един: Труднорешаеми ли са задачите от класа
NP?
Работата на Кук дава тласък на изследванията в тази област. Доказва
се NP-пълнотата на много известни задачи. В учебника на Джонсън са
описани над 300 такива задачи, разпределени в 12 категории (теория на
графите, мрежи, множества, съхранение и търсене на данни,
математическо програмиране, алгебра, теория на числата, логика и др.).
Ако само за една от тях бъде доказано, че може, или че не може да бъде
решена с полиномиален алгоритъм, то това ще следва и за всички останали
Доказателството на NP-пълнотата на дадена задача П се извършва по
следния начин:
1. доказва се, че П NP;
2. избор на известна NP-пълна задача П’;
3. свеждане на П’ към П;
4. доказателство, че сводимостта е полиномиална.

ЗАКЛЮЧЕНИЕ
Теорията на NP-пълните задачи ни предоставя математическия
апарат, необходим за пълноценното изследване на всеки компютърен
алгоритъм. Темата за оценка и сложност на алгоритмите е свързана с
оценката на времето за работа и оценка на ефективността на алгоритмите,
което има несъмнено значение за практическата им реализация.

18
Използвана литература:
1. Миланов, П. и кол., Теория на алгоритмите и оптимизация, Благоевград,
ЮЗУ „Неофит Рилски“, http://bioinfo.swu.bg/project/kniga%20algoritmi.pdf
2. Наков, П., П. Добриков, Програмиране = ++ Алгоритми,
http://www.programirane.org/
3. Марков, М., Теория на алгоритмите,
https://learn.fmi.uni-sofia.bg/pluginfile.php/99103/mod_resource/content/1/lec-
algs.pdf
4. Марков, М., Лекции по алгоритми,
https://learn.fmi.uni-sofia.bg/pluginfile.php/135158/mod_resource/content/10/
lec-algs.pdf

19

You might also like