Mihai Budiu -- mihaib+@cs.cmu.edu
http://www.cs.cmu.edu/~mihaib/
18 august 1999
Articolul de față atacă o problemă foarte importantă în informatică, din mai multe puncte de vedere (din mai multe puncte de vedere este atît importanța cît și atacul meu). Problema aceasta este extrem de importantă pentru practicieni, pentru că apare în extrem de multe contexte în viața ``reală'', sau foarte multe alte probleme reale se pot reduce la ea (definim precis noțiunea de ``reducere'' undeva mai jos).
Problema este însă și mai importantă pentru teoreticieni, din mai multe motive. În primul rînd, nu se cunoaște nici o soluție eficace pentru această problemă, în cazul ei cel mai general. Toate metodele de calcul sunt extrem de costisitoare, făcînd imposibilă rezolvarea unor instanțe de dimensiuni mari. Pe de altă parte, complexitatea algoritmilor cunoscuți îndepărtează speranța că vreodată vom avea suficiente resurse pentru a rezolva instanțe mari ale acestei probleme; chiar creșteri ale performanței calculatoarelor de milioane de ori ar permite creșterea numărului de variabile din cu doar cîteva zeci de unități (iar problemele practice pot avea zeci de mii de variabile).
În fine, mult mai interesant, problema aceasta este într-un anume sens ``cea mai grea problemă din clasa ei de probleme''. Sunt extrem de multe alte probleme practice care sunt echivalente, sau mai ușoare, decît această problemă. Teoreticienii au demonstrat că dacă, putem soluționa problema de față în mod eficient, atunci toate celelalte probleme echivalente sau mai ușoare se pot rezolva extrem de eficient, în mod imediat. O consecință surprinzătoare a acestui fapt ar fi că criptografia ar fi inexistentă, pentru că orice metodă de criptare ar putea fi spartă în mod eficient.
În fine, problema aceasta are implicații interesante pentru filozofie, pentru că, într-un anume sens, ne ajută să măsurăm diferența dintre geniu și normalitate, dintre creativitate și simpla înțelegere pasivă. Existența unei soluții eficace pentru această problemă ar avea de pildă consecința surprinzătoare că pentru fiecare teoremă din matematică există o metodă foarte eficace de a o demonstra cu calculatorul; asta ar transforma întreaga muncă de creație a matematicienilor într-o treabă de rutină, pe care o poate face o mașină de calcul.
Problema aceasta este piatra de temelie a unei ramuri a informaticii numită ``teoria complexității'', căreia intenționez să-i consacru în viitor un întreg articol. Existența unei soluții eficace pentru problema din acest articol este considerată aproape unanim cea mai importantă problemă nerezolvată din știința calculatoarelor.
Și acum, că v-am trezit curiozitatea, să purcedem să vedem cum se înlănțuie toate aceste fapte.
Trebuie dintru început să facem distincție dintre o clasă de probleme și instanțele problemei. O clasă de probleme este o colecție de instanțe înrudite.
În general, un program pentru calculator (un algoritm) rezolvă orice instanță dintr-o clasă. De exemplu, în procesorul Pentium se află o unitate aritmetică folosită pentru a efectua adunări. Această unitate aritmetică rezolvă următoarea clasă de probleme: ``care este rezultatul adunării a două numere mai scurte de 32 de biți?'' O instanță a acestei probleme este ``cît face 3+7?''. Observați că unitatea aritmetică poate rezolva orice instanță. Nu ar fi foarte folositor dacă ar putea rezolva numai întrebarea ``cît e 3+7?''.
În general nu avem nevoie să rezolvăm chiar toate instanțele, dar pentru că e prea complicat să construim mașini speciale pentru fiecare instanță (și nu prea practic), facem mașini care rezolvă clase întregi de probleme (adică pot rezolva orice instanță).
Observați că clasa de probleme: ``fiind dat un număr n, care este rezultatul adunării a oricare două numere de n biți'' este o clasă mai largă decît cea rezolvată de Pentium, pentru care n=32. Putem scrie un program care să calculeze în principiu suma oricăror două numere, oricît de lungi ar fi ele. Cum arată programul, am învățat cu toții în școala primară, folosind tabla adunării. Folosind această tablă putem aduna numere oricît de lungi (dacă nu obosim).
În general, teoreticienii studiază astfel de clase de probleme, în care ``dimensiunea'' datelor de intrare nu este limitată apriori de nici o valoare. Ei sunt interesați să afle dacă putem scrie programe care să funcționeze și atunci cînd nu se cunoaște această dimensiune dinainte.
Este de altfel o idee foarte bună ca algoritmii pe care îi implementați să fie proiectați în așa fel încît să poată trata probleme oricît de mari. Orice limitare arbitrară impusă datelor de intrare se poate dovedi extrem de neplăcută mai tîrziu; în definitiv din asta a și provenit celebrul bug Y2K: programatorii nu au permis programelor să manipuleze decît date într-un interval de 100 de ani, ceea ce are consecințe economice uriașe la ora actuală (companiile cheltuiesc acum zeci de miliarde de dolari să descopere și corecteze astfel de imperfecțiuni).
Definim un algoritm astfel: o procedură de calcul efectiv, care rezolvă orice instanță dintr-o anumită clasă de probleme. Nu uitați că întotdeauna algoritmul este legat de clasă; pentru clase diferite putem avea algoritmi diferiți, chiar dacă clasele sunt înrudite. De exemplu, pentru clasa de probleme: ``care este suma a două numere mai mici ca 100?'' putem face un algoritm foarte simplu, care pre-calculează toate sumele posibile, după care caută rezultatul într-o tabelă bidimensională de 100*100 de numere. Desigur, algoritmul risipește o mulțime de spațiu, dar răspunde practic instantaneu. Este clar că acest algoritm nu se poate generaliza pentru a face orice adunare, în care lungimea numerelor nu este limitată dinainte.
Voi introduce acum clasa de probleme care constituie subiectul principal al acestui articol. Pentru început însă voi trece în revistă rapid funcțiile boolene, pentru că problema noastră este legată de ele.
Georges Boole a trăit între 1815 și 1864. În istoria științei el este cunoscut mai ales prin rezultatele sale matematice, ca creator al algebrei boolene (numită, evident, în cinstea sa). Aceasta este o algebră extrem de simplă, care operează cu numai două valori, 0 și 1.
Dar Boole este extrem de important și în istoria filozofiei, pentru că algebra lui a fost creată în încercarea de a formaliza în limbajul matematicii logica aristotelică clasică. Georges Boole este considerat de unii părintele logicii matematice. Boole era interesat să reformuleze legile gîndirii umane în termeni matematici.
Algebra booleană operează cu valori de adevăr, din care avem numai două: adevărat și fals. Orice variabilă are la un moment dat exact una dintre aceste două valori. 0 este folosit pentru a denota falsul, iar 1 pentru adevărat.
Boole a introdus anumite operații pentru manipularea valorilor boolene: operațiile de ``și'' logic (), ``sau'' logic () și negație logică (). (Putem introduce și alte operații, dar acestea sunt cele mai importante.)
Putem combina valori boolene folosind aceste operații, și putem calcula valoarea de adevăr a unor propoziții complicate într-un mod foarte limpede.
De exemplu, dacă avem două propoziții logice oarecare, a și b, putem vorbi de propoziția (a și b), care este adevărată numai cînd ambele propoziții sunt adevărate. Modul de operare al lui poate astfel fi descris în același fel ca cel al operațiilor aritmetice obișnuite, cu o tabelă (cum e tabla adunării), așa cum arată figura 1.
De exemplu, dacă avem propozițiile ``Afară plouă'' și ``E ora 7'', propoziția formată din conjuncția acestora (adică legarea celor două cu ``și'') este ``E ora 7 și afară plouă''. În mod evident, această propoziție este adevărată doar dacă ambele enunțuri mai simple sunt adevărate, justificînd tabela operației ``și''.
|
O altă operație logică este cea numită ``sau'': rezultatul este adevărat dacă măcar una dintre valorile combinate este adevărată1.
În fine, avem operația de negare (``nu''), care inversează valoarea de adevăr a unei propoziții2.
Operațiile boolene sunt asociative și comutative. Putem astfel scrie prescurtat formule boolene complicate, de genul:
Algebra booleană este enorm de importantă în calculatoare; componentele electronice de bază din care este construit un calculator sunt ``porțile logice'', care implementează exact aceste operații, manipulînd curenți electrici pentru a reprezenta valori boolene (anumite potențiale fiind alese în mod convențional pentru a reprezenta valorile 0 și 1). Altfel de dispozitive sunt foarte ușor de construit din tranzistoare, care apoi se pot construi la o scara minusculă, înghesuind milioane pe o pilulă de siliciu.
Dacă ni se dă o formulă booleană și o serie de valori pentru variabilele care o compun, putem scrie un algoritm foarte simplu și eficient care evaluează formula. Acesta este chiar algoritmul bine-cunoscut folosit pentru evaluarea expresiilor aritmetice, cu diferența că operează doar cu valori boolene.
Acum suntem în măsură să formulăm problema care ne interesează, numită problema satisfiabilității, (satisfiability), pe scurt SAT: ``fiind dată o formulă booleană, există o atribuire a variabilelor care o compun care face formula adevărată?''.
Îmi aduc aminte de un banc: ``Cică un inginer și un matematician dimineața își făceau ceaiul, aplicînd următorul algoritm: <<Ia ibricul, umple-l cu apa, pune-l pe foc, fierbe apa, pune plicul, toarnă în cană>>. Dar într-o zi, nevestele lor le lasă ibricul cu apă înauntru. Ce fac cei doi? Ei bine, inginerul aplică algoritmul de la al doilea pas: pune-l pe foc, fierbe apa, pune plicul, toarnă în cană. În schimb matematicianul varsă apa în chiuvetă și reduce problema la cea precedentă.''
Bancul este amuzant (găsesc eu), dar conceptul de reducere este cu adevărat util. Cel puțin în calculatoare, are extrem de multe aplicații. Aici ne vom uita la doar una dintre ele.
Adesea putem abstractiza o problemă și o putem enunța într-un alt fel; de fapt, chiar pentru a rezolva probleme practice, în general le abstractizăm în termeni matematici, după care aplicăm tehnici din domeniul matematicii.
Figura 1 ilustrează noțiunea de reducere pentru probleme în teoria complexității.
Ei bine, se întîmplă că problema SAT este o problemă extrem de expresivă; putem enunța o mulțime de alte probleme sub forma SAT.
Voi ilustra aici aici o reducere foarte simplă, între o problemă numită ``colorarea unei hărți cu două culori'' și SAT.
Să presupunem că avem o hartă pe care vrem să o colorăm în așa fel încît oricare două țări alăturate să aibă culori diferite.
Asociem fiecărei țări o variabilă booleană. Apoi pentru fiecare vecinătate dintre două țări creăm o clauză3: dacă țara A este vecină cu țara B, atunci creăm clauza . Vom folosi valoarea 0 pentru a denota o culoare, și valoarea 1 pentru cealaltă. Formula de mai sus spune că țările A și B nu pot avea aceeași culoare simultan, pentru că dacă variabilele A și B au aceeași valoare, atunci clauza de mai sus va fi falsă.
Formula booleană pe care vrem s-o satisfacem este conjuncția tuturor acestor clauze.
Dacă formula este satisfiabilă, culorile țărilor corespund valorilor variabilelor boolene respective. Dacă formula nu este satisfiabilă, atunci nici problema originală nu are soluție.
Deci dacă rezolvăm problema satisfiabilității, atunci putem rezolva și problema colorării unei hărți cu două culori. Aceasta este deci o reducere de la una la cealaltă.
Iată niște exemple de probleme cu însemnătate foarte mare pentru activitatea umană, care se pot reduce la SAT cu ușurință:
Și lista ar putea continua cu multe alte probleme.
Vedeți, faptul că aceste probleme sunt toate reductibile la SAT ne spune două lucruri:
Din păcate, vom vedea puțin mai jos că speranțele noastre de a rezolva aceste probleme sunt foarte reduse.
Aparent SAT este o problemă banală, și algoritmul pentru a o rezolva este foarte simplu:
Într-adevăr, algoritmul de mai sus este corect; dacă există o distribuție de valori pentru care formula este adevărată, o va găsi. Dacă nu este nici una, atunci va da corect răspunsul ``nu'' odată ce a examinat toate posibilitățile. Acest algoritm este corect, dar nu este și eficient. Dacă nu credeți acest lucru, atunci încercați să executați algoritmul pentru o formulă cu 64 de variabile.
Din modul în care am expus algoritmul deficiența este vizibilă:
dacă avem k variabile, atunci trebuie să incrementăm de la 0 la
valoarea maximă un contor cu k cifre. Ori acest contor trebuie
incrementat de 2k ori. Pentru 64 de pildă putem aproxima
210 = 103, deci 264 = 1018 * 16
= 1019. Dacă un calculator execută o instrucțiune la o
nanosecundă (adică are un ceas de un gigahertz), îi vor trebui
1019/109 = 1010 secunde pentru a
efectua doar incrementarea. Un an are cam 3 * 107 secunde,
deci durata calculului ar fi de circa 30 de ani!
Dacă asta vi se pare o bagatelă, atunci încercați să rezolvați o
problemă doar puțin mai mare, să zicem cu 70 de variabile (10% mai
mare). Ei bine, asta o să dureze de 64 de ori mai mult, adică 1800
de ani!
Desigur, timpul acesta de rulare va fi petrecut doar dacă formula pe
care o verificați nu este satisfiabilă, pentru că atunci contorul
trebuie să treacă prin toate valorile.
Teoria complexității măsoară timpul de execuție al unui algoritm
ca o funcție de cantitatea de date oferite spre prelucrare. Dacă
măsurăm formula SAT de la intrare după numărul de variabile n,
atunci algoritmul de mai sus se poate executa pentru o durată
proporțională cu 2n, pentru anumite formule.
Cea mai ades folosită metodă de măsurare a complexității unui
algoritm consideră cel mai defavorabil caz posibil. De exemplu,
printre formulele SAT de lungime 2 exista multe care se pot rezolva
imediat, pentru că din prima încercare putem spune că formula este
satisfiabilă (de exemplu formula
). Dar există cel
puțin o formulă de lungime 2 pentru care algoritmul trebuie să
facă toate cele 4 încercări posibile. Din cauza aceasta, spunem
că complexitatea algoritmului de mai sus este de ordinul 2n, unde
în mod implicit cu n se notează dimensiunea datelor de
intrare5.
Vedeți, am spus că complexitatea este ``proporțională cu'', și nu
``exact'' 2n. Această caracterizare este destul de precisă,
însă, pentru că, indiferent care este coeficientul de
proporționalitate, (care de altfel depinde de viteza procesorului și
de alți factori), modul în care acest algoritm se comportă pentru
probleme foarte mari este același: este complet ne-practic.
Teoria complexității consideră că orice clasă de probleme pentru
care complexitatea unui algoritm este proporțională cu un o funcție
polinomială este eficace; prin contrast, atunci cînd complexitatea
este proporțională cu o funcție exponențială (ca mai sus),
problema este considerată nerezolvabilă.
Pentru ilustrație, algoritmii cei mai buni care sortează în ordine
crescătoare un șir de n numere, au un timp de execuție
proporțional cu n log n, care este o funcție mai mică decît
polinomul n2 (pentru că funcția logaritmică crește mai încet
decît orice polinom). Problema sortării are deci o soluție
eficace.
Deci algoritmul de mai sus pentru SAT nu este prea eficace, cel puțin
dacă avem intenția de a rezolva probleme foarte mari. Înseamnă
asta că problema este practic nerezolvabilă?
Desigur, nu. Dacă am face această afirmație, ar fi ca și cum am
zice că nu putem ajunge la Ploiești de la București decît după 10
zile, pentru că așa am ajuns la un moment dat mergînd prin Japonia.
Faptul că avem un algoritm lent pentru o problemă nu înseamnă că
problema este grea. Poate există un alt algoritm, care rezolvă
problema mult mai bine!
Algoritmul de mai sus nu exploatează în nici un fel înfățișarea
formulei. De pildă, dacă formula conține următoarele două
clauze: a și , putem spune imediat că formula nu este
satisfiabilă, pentru că, orice valoare ar avea a, și independent
de valorile tuturor celorlalte variabile, rezultatul formulei va fi
tot 0.
E adevărat că putem scrie tot felul de algoritmi mai inteligenți,
dar pînă la ora actuală nimeni nu a reușit să găsească un
algoritm eficace pentru SAT, care, pentru orice instanță a problemei
să ofere răspunsul într-un timp mai scurt decît cel exponențial.
Mai mult decît atît, sunt dovezi foarte convingătoare (dar nu și o
demonstrație exactă) că SAT nu admite o soluție eficientă. Poate
să pară intuitiv clar că SAT are nevoie de foarte mult timp, dar nu
există nici demonstrația inversă, cum că SAT nu poate fi
rezolvată rapid.
``Ei, și ce?'' o sa întrebați. Cine are nevoie de SAT? Din
păcate, foarte multă lume.
Am văzut mai sus că foarte multe probleme se pot reduce la SAT.
Asta înseamnă că dacă am găsi o soluție eficientă pentru SAT,
am putea rezolva eficace și aceste probleme practice.
Toate problemele de mai sus au o trăsătură foarte interesantă: nu
știe nimeni cum să le găsească o soluție, dar de îndată ce
cineva ne-ar da un răspuns pentru o instanță, am putea verifica
foarte repede dacă acela este răspunsul corect.
Teoria complexității definește astfel o mulțime de probleme
numită NP (de la Nedeterminist-Polinomial, o denumire tradițională,
care are o justificare despre care nu vom discuta acum): clasa NP este
compusă din toate problemele pentru care putem verifica foarte
eficient dacă un anumit răspuns este o soluție corectă (eficient
înseamnă, din nou, că verificarea durează un timp polinomial în
mărimea problemei pe care o avem de rezolvat). SAT și toate
problemele de mai sus fac parte din NP.
Se definește de asemenea clasa tuturor problemelor pentru care știm
să calculăm răspunsul exact într-un timp scurt, polinomial
în lungimea datelor de la intrare. Această clasă este denumită
simplu, P6.
De exemplu, problema circuitului hamiltonian este în clasa NP pentru
că, dacă cineva ne dă o listă de orașe putem verifica foarte
rapid dacă această listă formează sau nu un circuit hamiltonian.
Pentru a face asta verificăm că fiecare oraș apare o singură dată
în listă, că toate orașele de pe hartă apar, și că între
fiecare două orașe consecutive din listă chiar există un drum
direct. Prin definiție deci, această problemă este în clasa NP.
Nimeni nu cunoaște însă un algoritm eficient pentru a decide dacă
un ciclu hamiltonian există, deci nu știm dacă această problemă
este în clasa P.
Ei bine, toate problemele din P fac parte și din clasa NP. Asta
pentru că dacă ni se dă răspunsul la o problemă din P, pentru a
verifica dacă este corect nu facem decît să executăm algoritmul
eficace pentru a găsi soluția, și să comparăm soluția oferită
cu cea calculată. Figura 2 rezumă
starea cunoștințelor noastre la ora actuală.
Ar fi minunat dacă am putea găsi soluții eficiente pentru toate
problemele care sunt în NP, mai ales că multe dintre ele sunt
probleme de o mare importanță economică și practică.
În 1971 Stephen Cook a demonstrat7 o teoremă celebră, care arată că orice problemă din NP se reduce la SAT. Am văzut mai sus că
anumite probleme sunt reductibile la SAT; Cook a arătat printr-un
argument ingenios că orice altă problemă am avea (chiar cele
ne-formulate încă), dacă putem verifica soluțiile acelei probleme
într-un mod eficace (deci dacă problema este în clasa NP), atunci
problema este reductibilă la SAT.
Asta înseamnă că, dacă ni se dă o instanță a unei probleme
oarecare din NP, putem construi în mod automat o instanță a unei
probleme SAT. Construcția aceasta însăși se poate face într-un
mod foarte eficace (adică putem face construcția în timp
polinomial). Construcția garantează faptul că orice soluție a
problemei SAT construite corespunde unei soluții a problemei reale.
Astfel, putem acum rezolva problema SAT în locul problemei originale,
și apoi putem ``decodifica'' răspunsul.
Ce ne spune teorema lui Cook?
Ne spune în primul rînd că SAT este problema ``cea mai
importantă'' din clasa NP, pentru că, dacă o putem rezolva
eficient, putem rezolva toate celelalte probleme din NP. De fiecare
dată cînd găsim o problemă în NP, nu avem decît să facem o
reducere a problemei la SAT, să rezolvăm problema SAT, și apoi să
convertim înapoi răspunsul în termenii problemei originare. Acest
lucru este întotdeauna posibil.
Pe de altă parte, dificultatea găsirii răspunsului la problema
originară este clar mai mică decît dificultatea rezolvării
problemei SAT (avînd un răspuns la SAT obținem un răspuns la
problema originară, dar nu neapărat și invers).
Putem deci considera în acest sens SAT ca fiind cea mai grea
problemă din NP. Din aceste motive, SAT este numită o problemă
NP-completă. Acesta este un rezultat deosebit de important,
pentru că ne spune că mai curînd vom găsi algoritmi eficienți
pentru oricare altă problemă din NP decît pentru SAT. Pentru că
foarte multe din celelalte probleme sunt încă nerezolvate, nu sunt
prea multe șanse să rezolvăm SAT.
Deci SAT este cea mai grea problemă. Partea proastă este că în
decursul vremii oamenii au reușit să arate că există o sumedenie
de alte probleme din NP care... sunt mai grele ca SAT!
Pentru a demonstra că o problemă este ``mai grea'' ca SAT nu trebuie
decît să facem lucrul invers: să reducem pe SAT la acea problemă.
Asta ne permite să codificăm orice instanță SAT ca pe o instanță
a acelei probleme, și ne permite să rezolvăm SAT punînd
întrebări despre instanțe ale celeilalte probleme.
Dacă o problemă este mai grea decît SAT, din moment ce SAT este
deja cea mai grea problemă, ajungem la concluzia că acea problemă
este la fel de grea ca SAT; dificultatea ambelor probleme este
aceeași. Cele două probleme sunt practic echivalente.
Demonstrarea că o problemă este mai grea decît SAT nu este în
general o treabă ușoară. Cu toate acestea, toate problemele
enunțate în lista de mai sus (circuitul hamiltonian, problema
planificării, problema rucsacului, etc.), repet, probleme de o
deosebită importanță practică, sunt demonstrate a fi mai grele
decît SAT.
Toate aceste probleme sunt deci la rîndul lor NP-complete!
Închei aici prima parte a prezentării mele despre SAT; acesta este
partea cea mai tehnică, deși evită orice demonstrație. În
realitate teorema lui Cook nu este prea complicată, și în afară de
noțiunile expuse în acest text nu mai este nevoie de mare lucru
pentru a o înțelege. Cititorul interesat este trimis de pildă la
cartea lui Papadimitriou ``Computational Complexity'', sau la cartea
lui Weinker & Davis ``Computability, Complexity and Languages''.
În partea a doua a acestui text (în numărul viitor) voi discuta
despre alte implicații ale problemei SAT, despre interpretarea
filozofică a clasei NP, despre soluțiile practice euristice care
s-au găsit pentru a rezolva unele dintre instanțele acestei
probleme, și despre niște proprietăți statistice interesante ale
densității soluțiilor problemei.
Mesajul cel mai important al acestui text se află însă în partea
de față. Am văzut în acest text că între anumite probleme
computaționale putem face reduceri, astfel încît să transformăm
soluțiile uneia în soluțiile celeilalte. Am văzut de asemenea că
există o clasă foarte interesantă de probleme (numită NP) pentru
care știm să verificăm eficient dacă ceva este o soluție, dar nu
avem nici o idee despre cum să găsim o soluție. Am văzut și că
SAT este o astfel de problemă, și că orice altă problemă din NP
se poate reduce la SAT, făcînd din SAT o problemă NP-completă.
Dacă cel puțin un cititor găsește aceste fapte demne de interes,
sunt satisfăcut; mai satisfăcut decît probabil SAT va fi vreodată.
În numărul anterior din PC Report am consacrat un articol unei
probleme pe care am numit-o cu emfază ``cea mai importantă problemă
nerezolvată din informatică''. Textul de față este o continuare;
cititorii care nu posedă primul episod sunt invitați să obțină o
copie a textului din pagina mea de web.
Problema de care vorbim se numește problema ``satisfiabilității'',
notată pe scurt cu SAT. Un enunț foarte intuitiv, pentru cei cu
oarecare noțiuni despre arhitectura calculatoarelor, este următorul:
``Fie un circuit digital combinațional (adică fără cicluri),
construit din porți logice ``sau'', ``și'', ``nu'', care are o
singură valoare la ieșire. Există o combinație de valori la
intrarea circuitului care cauzează ieșirea circuitului să fie 1?''.
În textul anterior am formulat această întrebare pentru o formulă
booleană (o formulă care operează cu operatorii algebrei boolene,
introdusă de George Boole); cele două formulări sunt echivalente,
deoarece porțile logice ale unui circuit combinațional corespund
operațiilor boolene, în așa fel încît fiecare formulă corespunde
unui circuit și invers.
De fapt găsirea unui algoritm care să ofere răspunsul corect la
această problemă nu este un lucru prea greu; neplăcerea constă în
faptul că nu se cunoaște nici un algoritm eficace, care să
fie capabil să rezolve probleme mari într-un timp scurt. Toți
algoritmii cunoscuți, inclusiv cei prezentați ceva mai jos, au
trăsătura indezirabilă că există instanțe ale problemei pentru
care timpul de execuție este exponențial în dimensiunea problemei.
Asta înseamnă că, dacă adăugăm o singură variabilă în plus,
timpul necesar rezolvării se dublează. Este ușor de văzut că
acest ritm de creștere este covîrșitor, și că întreaga
capacitate de calcul a omenirii nu este suficientă pentru a trata
probleme de dimensiuni nu prea mari (zeci de mii de variabile).
Existența unei soluții eficace pentru problema satisfiabilității
nu este considerată cea mai importantă din întreaga informatică
doar de către niște teoreticieni amatori de curiozități.
Importanța practică a acestei probleme este enormă: foarte foarte
multe probleme practice sunt echivalente ca dificultate cu problema
satisfiabilității. Michael Garey și David Johnson au alcătuit în
1979 o culegere de probleme, toate echivalente cu SAT: ``Computers and
Intractability: A Guide to the Theory of NP-Completeness'', publicată
de editura W. H. Freeman. Între timp lista aceasta a crescut mult;
la ora aceasta cuprinde cîteva mii de exemple.
Toate aceste probleme au o trăsătură foarte interesantă în comun:
pentru nici una nu se cunoaște o soluție eficientă (adică un
algoritm eficient care să rezolve toate instanțele acestor
probleme), dar nici nu există argumente că astfel de soluții nu ar
exista. Dacă măcar am ști că nu există soluții eficiente, poate
am fi mai împăcați, chit că aflarea soluției ar putea avea
importanță economică de miliarde de dolari. Dar nu știm că nu
putem, așa că suntem nervoși.
Mai mult decît atît: dacă noi căutăm soluția și cineva vine și
zice: ``știu răspunsul: formula ta nu este satisfiabilă''. Noi îl
putem provoca: ``dovedește''. Ei bine, atunci acel cineva poate
produce o dovadă simplă că are dreptate și că știe într-adevăr
soluția. Cu alte cuvinte, pentru aceste probleme există dovezi
simple și ușor de verificat că ceva este o soluție8.
Există o denumire specială pentru problemele pentru care putem
verifica imediat dacă o pretinsă soluție este corectă: toate
aceste probleme formează așa numita clasă NP, de la
nedeterminist-polinomial. SAT este o astfel de problemă, pentru că
dacă cineva ne arată intrările unui circuit putem imediat vedea
dacă circuitul va obține un ``1'' la ieșire.
În prima parte a acestui articol menționam că în 1971 Cook a
demonstrat că SAT este cea mai grea problemă din clasa NP, în
sensul că orice altă problemă din NP se reduce la SAT. Din cauza
asta SAT este numită o problemă NP-completă, la fel ca toate
celelalte probleme echivalente prezentate în culegerea lui Garey și
Johnson.
Prin contrast, clasa tuturor problemelor pe care le putem rezolva
eficient (care sunt definite ca probleme pentru care cunoaștem
algoritmi al căror timp de rulare este proporțional cu o funcție
polinomială în dimensiunea problemei) se numește P.
NP nu este nici pe departe clasa problemelor celor mai dificile pe
care le cunoaștem; există clase de complexitate mult mai grele, ca
de pildă EXP, clasa problemelor pentru care cu siguranță nu există
soluții ne-exponențiale ca timp. Există chiar clasa problemelor
nedecidabile, pentru care nu există... nici un fel de
soluție9!
De fapt teoria complexității demonstrează existența a tot felul de
ierarhii de clase de complexitate și studiază relațiile dintre
feluritele clase. Adesea relațiile sunt de incluziune, așa cum avem
între P și NP: orice problemă P este și una NP; uneori însă
relațiile sunt mai bizare, sau sunt condiționate de alte relații
(ceva de genul: dacă clasa A e diferită de clasa B, atunci clasa D e
tot una cu clasa C). Teoria complexității clasifică problemele în
funcție nu neapărat de timpul necesar soluționării, ci și
în funcție de alte resurse necesare, cum ar fi: cantitate de memorie
necesară (numită și spațiu), număr de biți aleatori,
porți logice, ``adîncimea'' unui circuit care rezolvă problema,
etc.
Lumea face atît de mult tam-tam vis-a-vis de NP nu numai pentru că
există în NP multe probleme practice importante (căci și celelalte
clase de complexitate conțin probleme practice importante), ci și
pentru că pentru problemele din NP există speranța că ar
putea fi rezolvate rapid, dar nimeni nu știe cum. Dacă am ști că
nu se poate, am fi probabil mai relaxați, dar așa e ca și cum
soluția ne scapă printre degete.
În acest paragraf vom discuta o trăsătură foarte interesantă a
lui SAT. SAT, așa cum este ea formulată, este o ``problemă de
decizie''; acest nume tehnic înseamnă doar că răspunsul trebuie
să fie doar ``DA'' sau ``NU'': dacă ni se dă un circuit, trebuie
să spunem doar dacă circuitul este satisfiabil sau nu, dar nu și
cum este satisfiabil.
Aparent capacitatea de a rezolva o problemă de decizie nu e prea
utilă: dacă un profesor la școală are voie să pună numai
întrebări la care studentul să răspundă numai cu ``DA'' sau
``NU'', nu pare să poată verifica prea bine cunoștințele
studenților în acest fel (în definitiv dacă aceștia dau cu banul
au șansa de a răspunde 50% corect, deci s'a ia not'a de trecere
fără nici un efort).
În realitate însă această limitare la probleme de decizie nu este
o constrîngere prea severă. Folosind un procedeu numit
auto-reducere putem transforma mai multe răspunsuri DA-NU într-o
soluție completă (adică putem afla și cum circuitul poate fi
făcut satisfiabil, pentru care intrări). Iată cum:
Să presupunem că avem la dispoziție un algoritm care rezolvă
rapid și corect SAT, în sensul că răspunde întotdeauna ``DA''
cînd i se dă o formulă SAT care poate genera ``1'' și ``NU'' cînd
pentru toate intrările posibile ale circuitului rezultatul este
``0''.
Vrem să găsim o serie de intrări pentru care rezultatul este
``1'', dacă acestea există.
Putem atunci proceda în acest fel :
După atîția pași cîte variabile avem, obținem pentru fiecare
variabilă o valoare.
(Putem apoi verifica dacă algoritmul nu a mințit, testînd valoarea
formulei cu valorile obținute. Același truc îl poate face și
profesorul pentru a testa un student numai cu răspunsuri ``DA'' și
``NU'': întreabă în felul următor: ``este prima literă din
capitala Braziliei A'', apoi, dacă răspunsul studentului este
``da'', întreabă de a doua literă, altfel încearcă B pentru
prima, etc. Deși metoda este incomodă, este pe deplin în
concordanță cu regulile jocului, și aproape infailibilă:
profesorul obține cîte un bit de la student prin fiecare întrebare.
Dacă studentul știe răspunsul, profesorul trebuie să obțină toți biții corecți, și nu numai unul.)
Metoda auto-reducerii este extrem de puternică: ne permite să
folosim un potențial algoritm pentru a rezolva SAT pentru a rezolva o
sumedenie de alte probleme, aparent mult mai complicate (de exemplu,
chiar problema de a găsi valorile care fac o formulă satisfiabilă).
Suntem înclinați să afirmăm că există o diferență
substanțială între (1) a înțelege ceva și (2) a crea ceva nou.
Aceste două feluri de activitate sunt net diferite: spectatorul unei
opere de artă apreciază rezultatul, dar efortul artistului pentru a
crea acea operă pare mult mai impresionant. Putem spune că
aprecierea este un algoritm de clasă P, dar creația este unul de
clasă NP: putem aprecia rezultatul, dar nu știm cum să-l producem.
Dacă vă deranjează excursul într-un domeniu care este rezervat
subiectivității totale (arta), atunci putem face exact aceeași
analogie în matematică: între (1) a demonstra o teoremă și (2) a
citi o demonstrație făcută de altcineva și a ne convinge de
corectitudinea ei, pare să fie din nou o diferență de natură.
Actul (1), al demonstratorului este un act creativ, de căutare
într-o enormă masă de posibilități, pe cînd actul (2), al
discipolului, care recitește demonstrația, este relativ simplu,
constînd în a verifica faptul că fiecare pas logic făcut în
demonstrație respectă regulile de inferență ale logicii.
Din nou avem aceeași antiteză, între actul de a genera (care poate
fi asimilat cu clasa NP) și actul de a verifica (care poate fi
asimilat cu clasa P)10.
Cum spuneam, nu există o demonstrație riguroasă că P NP,
dar nici una că P=NP. Suntem supuși unei tensiuni dureroase:
Dacă P=NP, am avea și alte consecințe extrem de importante, în
oarecare măsură devastatoare:
Pentru orice teoremă din matematică am putea scrie un program care
să găsească o demonstrație într-un timp nu mult mai mare decît
lungimea celei mai scurte demonstrație posibile (ar exista un polinom
P(x) astfel încît, dacă cea mai scurtă demonstrație posibilă
este de lungime x, algoritmul am găsi una în timp cel mult
P(x).).
Un astfel de algoritm ar folosi tehnica auto-reducerii pentru a deriva
o demonstrație corectă (ceva de genul: există o demonstrație care
începe cu litera ``A''? etc.)
Importanța acestei tehnici pentru demonstrarea automată a teoremelor
din matematică a fost observată de către logicianul Kurt Gödel
înca din 1956, într-o scrisoare adresată marelui matematician John
von Neumann, cu mult înainte ca teoria NP-complexității să existe
(și cu mult înainte să existe calculatoare de uz comun).
Decriptarea este la rîndul ei o problemă NP, dacă o formulăm
în felul următor: ``este acest șir de caractere criptarea unui text
cu sens''? Decriptarea este în NP, pentru că dacă cineva ne oferă
cheia de decriptare, putem obține textul criptat cu un algoritm
rapid; putem astfel verifica în timp polinomial că mesajul criptat
este într-adevăr rezultat din sursă.
Dar dacă P=NP, înseamnă că există un algoritm de timp polinomial
pentru SAT; îl putem folosi împreună cu metoda auto-reducerii
pentru a ``sparge'' orice mesaj! Deci, dacă P=NP, nu există
criptografie! Dintr-o dată, lumea arată cu totul altfel.
Pentru că SAT este o problema atît de dificilă multă lume s-a
străduit să creeze algoritmi de criptare bazați pe probleme
NP-complete. Din nefericire pînă la urmă toate metodele de
criptare bazate pe probleme NP-complete s-au dovedit a fi foarte ușor
de spart!
Asta e chiar o surpriză: creăm probleme dintr-o clasă reputat
dificilă, dar în practică reușim foarte des să le rezolvăm! Nu
e ceva putred la mijloc?
De fapt nimic nu ne garantează că NP este o clasă potrivită pentru
criptografie, pentru că ``tăria'' oferită de NP nu este aceeași cu
``tăria'' necesară criptografilor. Iată de ce: dacă ne ducem la
prima parte a acestui articol, vedem că complexitatea unei clase de
probleme este dată de cea mai grea problemă din clasă.
Astfel, dacă toate problemele din NP ar fi foarte ușoare, dar una
singură ar fi extrem de grea, noi tot am declara clasa NP ca fiind
grea. Asta se numește ``complexitatea în cazul cel mai rău''
(worst-case complexity).
Criptografii pe de altă parte nu vor ca unele probleme să fie grele
și altele ușoare; ei vor ca o problemă aleasă la
întîmplare să fie dificilă: nu ai nici un avantaj dacă există o
singură cheie care nu poate fi ``spartă'', pentru că este nevoie de
multe chei. Complexitatea ``medie'' a unei probleme trebuie să fie
ridicată pentru ca acea problemă să fie potrivită pentru
criptografie.
Clasa problemelor NP-complete nu pare să fie densă în probleme
grele; ea constă în probleme expresive: vă reamintiți că o
problemă este NP-completă dacă putem reduce orice altă problemă
din NP la ea; asta înseamnă că ``limbajul'' problemelor NP-complete
este foarte expresiv: ne permite să codificăm orice altă problemă.
La ora asta cei mai rezilienți algoritmi criptografici se bazează pe
probleme care nu sunt demonstrate a fi NP-complete, și care foarte
probabil nici nu sunt (dar, din nou, nu există încă nici o
demonstrație în acest sens). Cele mai bune candidate s-au dovedit
unele probleme de teoria numerelor. De fapt niște probleme absolut
banale (în sensul că pot fi explicate și unui școlar):
criptosistemul cu cheie publică RSA se bazează pe problema factorizării: dacă am un număr, cum să-l descompun într-un
produs de factori (primi).
Dacă vă grăbiți să sugerați algoritmul învățat la școală
pentru descompunere în factori primi, vă invit să-i analizați
complexitatea. Acel algoritm trebuie să încerce toate numerele
prime mai mici decît numărul pe care vreți să-l descompuneți.
Ori, pentru numărul n, există aproximativ
2n/log(2n) astfel de numere, adică un număr
exponențial. Asta înseamnă că algoritmul, deși funcționează binișor
pentru numerele oferite în clasă ca exercițiu, este absolut
inaplicabil, chiar cu cele mai performante calculatoare, atunci cînd
avem de-a face cu numere de sute de cifre.
O întrebare legitimă pe care ne-o putem pune este ``cînd este o
instanță a lui SAT dificilă''? Pentru a putea răspunde la
această întrebare, vom introduce niște noi elemente de
terminologie.
După cum am spus, o problemă din SAT constă într-o formulă
booleană (sau un circuit logic) care aplică operațiile ``sau'',
``și'' și ``nu'' logic unor variabile pentru a obține o singură
valoare, de un bit. Ce vrem să știm este dacă există valori ale
variabilelor manipulate care să facă formula ``1''.
Algebra booleană ne învață că fiecare formulă logică se poate
rescrie într-o formă mai simplă, în care:
O astfel de formă se numește ``forma normală conjunctivă'', sau
CNF (Conjunctive Normal Form), pentru că formula este o conjuncție
(și) de disjuncții (sau). De exemplu
este în forma CNF, pe cînd
nu este, pentru că avem o negație aplicată unei formule.
Se poate arăta că, chiar dacă formula este exprimată în forma
CNF, asta nu simplifică cu nimic sarcina lui SAT, problema
rămînînd NP-completă. Poate să vi se pară ciudat că s-ar putea
întîmpla altfel: în definitiv orice formulă poate fi scrisă sub
forma CNF; aparent noi rezolvăm aceeași problemă, doar scrisă
diferit: de ce ne-am aștepta ca complexitatea rezolvării să fie
alta dacă exprimăm formula altfel?
Ei bine, clasa de probleme depinde foarte mult și de felul în care
enunțăm problema; clasa SAT este diferită de clasa CNF-SAT: prima
clasă conține orice formulă logică, a doua numai formulele logice
care sunt scrise în forma normală conjunctivă. E adevărat că
orice formulă din prima clasă este absolut echivalentă cu una din a
doua clasă (în sensul că calculează aceeași funcție booleană),
dar cele două formule nu sunt identice.
Pentru a ne convinge că este important modul de exprimare, să notăm
că există și o formă de scriere a formulelor boolene, numită
``normală disjunctivă'', notată DNF, care e la fel cu CNF, dar la
care se schimbă semnele ``sau'' și ``și'' între ele. Se poate de
asemenea arăta că de asemenea orice formulă booleană se poate pune
în forma normală disjunctivă. Dar orice întrebare din DNF-SAT se
poate rezolva în timp polinomial! DNF-SAT este o clasă de probleme
mult mai ușoare decît cele din SAT sau CNF-SAT.
De ce stau lucrurile așa? Pentru că atunci cînd transcriem o
formulă din SAT în formă DNF-SAT, lungimea ei poate crește foarte
mult; dacă lungimea inițială a formulei era x și complexitatea
algoritmului era 2x, și dacă după traducere avem lungimea
n=2x și complexitatea n2 =
2x2, care este exponențială în x dar polinomială în n.
Iată încă un exemplu: problema de a găsi divizorii unui număr
este foarte grea dacă numărul este scris în baza 2 (sau orice altă
bază mai mare ca 2), dar este foarte simplă dacă numărul este
scris în baza 1. Dacă scriem numărul 100 folosind 100 de
bețișoare, atunci putem exhiba un algoritm de complexitate n2
care găsește toți divizorii lui 100 (de pildă algoritmul
învățat în școala primară). Dar, scriind un număr în baza 2,
folosim mult mai puține cifre (mai exact, pentru n folosim log n
cifre), deci chiar dacă aplicăm același algoritm, într-un caz
măsurăm timpul algoritmului relativ la n, iar în altul relativ la
log n$.
Din motive tehnice adesea cercetătorii lucrează cu o versiune a
problemei CNF-SAT, descrisă printr-un număr k: k-CNF-SAT. Avem
astfel 2-CNF-SAT, 3-CNF-SAT, etc. Clasa k-CNF-SAT constă din
formulele boolene scrise în formă CNF dar pentru care orice clauză
are cel mult k literali. De exemplu formula
este în 3-CNF-SAT, și în
4-CNF-SAT, dar nu în 2-CNF-SAT.
Se arată de asemenea, fără prea multă dificultate, că, pentru
orice k întreg, orice formulă booleană se poate aduce în forma
k-CNF-SAT.
Avem însă de a face cu un fenomen interesant: 1- și 2-CNF-SAT sunt
clase de probleme foarte ușoare: se cunoaște chiar un algoritm de
timp linear care rezolvă aceste probleme. Pe de altă parte toate
clasele CNF-SAT cu mai mult de doi literali pe clauză sunt
NP-complete!
Ce straniu! O diferență atît de mare de complexitate, și o
diferență atît de mică de formă. Acest fenomen se întîlnește
foarte adesea în teoria NP-complexității; sunt foarte multe
probleme pentru care o anumită variantă este simplă, dar de
îndată ce schimbi un pic enunțul o transformi într-o variantă
foarte dificilă. (De exemplu, întrebarea dacă putem colora o
hartă cu două culori, în așa fel încît țari vecine să nu fie
colorate la fel, este banală, dar aceeași întrebare cu numărul
trei este NP-completă. Știm că putem oricînd colora o hartă cu 4
culori, dintr-o teoremă extrem de celebră.)
După cum am spus mai sus, nu toate instanțele unor probleme
NP-complete sunt la fel de grele: numai cele mai dificile sunt foarte
grele; unele pot fi foarte ușoare. Un articol extrem de interesant,
care a făcut vîlvă, a asimilat comportarea formulelor boolene cu
fenomenele fizice care se petrec cînd un material face o tranziție
de fază, de exemplu de la lichid spre solid. Acest articol este o
colaborare între fizicieni și informaticieni, și a fost publicat
în revista Nature. Articolul este scris de Rémi Monasson, de la
laboratorul CNRS de fizică teoretică de la Paris, Riccardo Zecchina,
de la Centrul internațional pentru fizică teoretică de la Trieste,
Scott Kirkpatrick, de la laboratoarele T. J. Watson ale lui IBM, Bart
Selman, profesor la departamentul de calculatoare de la Universitatea
Cornell și Lidror Troyansky, de la departamentul de calculatoare al
universității evreiești din Ierusalim.
Articolul a făcut atîta vîlvă încît a apărut și în ziare; o
versiune de popularizare despre această temă puteți găsi de
exemplu în New York Times la
Articolul studiază care dintre problemele SAT sunt grele și care
sunt ușoare. Cantitatea aflată sub studiu este constrîngerea: cît de multe condiții sunt puse asupra unei
formule. Dacă o formulă este prea puțin constrînsă, are o
multitudine de soluții. Dacă formula este foarte constrînsă,
atunci nu are nici o soluție. Astfel de formule sunt foarte ușor de
rezolvat: pentru cele puțin constrînse putem găsi soluții aproape
la întîmplare, pe cele supra-constrînse le putem ușor demonstra ca
ne-avînd nici o soluție. Noțiunea de constrîngere poate fi
asimilată cu rangul unui sistem de ecuații lineare din algebră: un
sistem de ecuații care are mai multe variabile decît
ecuații11 are o
infinitate de soluții, un sistem care are mai multe ecuații decît
variabile nu are nici o soluție. Articolul asimilează
constrîngerea cu gradul de libertate al moleculelor dintr-un lichid,
respectiv solid: solidul este mult mai constrîns.
Constrîngerea poate fi măsurată pentru probleme k-CNF-SAT prin
raportul dintre numărul de clauze din formulă și numărul de
variabile. Iată intuitiv de ce lucrurile stau așa: să luăm o
clauză izolată și să o facem adevărată selectînd un literal
anume ca adevărat (să zicem ca acesta este literalul a). Dacă ne
uităm acum la toate celelalte clauze care conțin , ele vor
fi forțate să aleagă un alt literal să fie adevărat. Aceste
clauze sunt constrînse de prima, pentru că au mai puține
alegeri de făcut. Cu cît avem mai multe clauze, cu atît avem mai
multe constrîngeri reciproce.
Articolul citat face un studiu experimental asupra raportului
dintre numărul de clauze și numărul de variabile, folosind formule
generate aleator pe care le rezolvă prin forță brută. Graficul
din figura 3 arată dependența dificultății unei probleme
de acest număr. Cînd este un număr mic, problemele au
multe soluții, și sunt ușor de rezolvat. Cînd este mare
problemele nu au nici o soluție, și acest lucru este iarăși foarte
ușor de descoperit. Cînd atinge o valoare critică, (care
depinde de k), nu e clar dacă problemele au sau nu soluții, iar
determinarea acestui lucru este extrem de dificil. Pentru aceste
instanțe, algoritmi cunoscuți calculează foarte mult.
Voi încheia excursia în domeniul SAT prin expunerea soluțiilor
folosite pentru a rezolva aceste probleme în practică. Faptul că
nu putem să rezolvăm problemele în general nu înseamnă că nu
avem nevoie să o facem, ba chiar, așa cum am arătat, probleme
NP-complete apar adesea în practică. Așa că tot ce putem face
este să facem tot felul de trucuri care măcar rezolvă rapid
cazurile ușoare.
În mod paradoxal, cei mai eficace algoritmi din ziua de azi sunt doar
variante ale unor algoritmi deosebit de simpli. Trimitem cititorul
interesat la articolul lui Stephen Cook12 și David Mitchell ``Finding Hard Instances of the
Satisfiability Problem: A Survey'', publicată în DIMACS Series in
Discrete Mathematics and Computer Science în 1997.
Cel mai eficace algoritm general pentru SAT rămîne un algoritm
propus de M. Davis și H. Putnam în 1960! Algoritmul lor alege
succesiv un literal pe care îl face adevărat, după care șterge
toate clauzele care conțin acel literal, pentru că aceste clauze
sunt deja satisfăcute. În plus, literalul negat este eliminat de
peste tot din celelalte clauze, pentru că este deja fals.
Crucială este alegerea literalului, iar algoritmul lor sugerează
niște euristici care duc la rezultate foarte bune în
practică. Euristicile sunt reguli care fac un algoritm mai eficient
în general, dar despre care nu se poate demonstra că sunt
întotdeauna alegerea corectă; sunt deci ``ghiciri înțelepte''.
Metoda lor începe prin a elimina întotdeauna cele mai constrînse
clauze, adică cele care conțin un singur literal: este clar că
acesta trebuie să fie adevărat pentru a face formula adevărată.
Datorită eliminărilor, astfel de clauze apar și în cursul
evoluției algoritmului. Dacă nu sunt astfel de clauze, atunci un
literal este ales la întîmplare și se încearcă pe rînd ambele
valori posibile, 0 și 1, făcînd backtracking dacă o încercare
eșuează.
Acest algoritm face parte din ceea ce se numesc ``metode incomplete de
rezolvare'', pentru că nu funcționează pe probleme care nu sunt
satisfiabile, dar funcționează excelent pe cele care au soluții,
găsind foarte repede soluții. Acest algoritm a fost propus de Bart
Selman, acum la Universitatea Cornell (și unul dintre autorii
articolului de mai sus despre tranzițiile de fază).
Acest algoritm este foarte surprinzător pentru că este un algoritm
aleator: dă cu banul pentru a decide ce valoare trebuie să aibă
fiecare variabilă. De fapt acest algoritm este reprezentativ pentru
cercetarea curentă în teoria algoritmilor aleatori (pe care i-am
menționat pe larg într-un articol în PC Report din august 1997).
Algoritmul este următorul:
Am văzut în acest articol că există probleme al căror enunț este
banal, dar care sunt de fapt foarte greu de rezolvat în practică în
mod eficient. Această stare de fapt simultan ne bucură și ne
întristează: ne bucură pentru că dacă am putea rezolva aceste
probleme ușor, ar rezulta că unele din îndeletnicirile umane pe
care le prețuim cel mai mult, de exemplu creativitatea
matematicienilor, pot fi reduse la simple manipulări mecaniciste.
Ne-am întrista, pentru că de fapt ne dorim foarte mult să rezolvăm
aceste probleme, fiindcă importanță lor practică este enormă.
Dacă vă doriți un drum rapid spre faimă mondială, atunci una
dintre soluții este să rezolvați această întrebare: se pot sau nu
rezolva aceste probleme în mod eficient, sau, tehnic vorbind, este P
tot una cu NP? Dar, fiți preveniți, multe capete luminate s-au
luptat cu această enigmă și au fost toate înfrînte!
Complexitatea unui algoritm
Complexitatea unei probleme
NP
Teorema lui Cook
Va urma
Recapitulare
Satisfiabilitate
Importanța practică; NP
Alte clase de complexitate
Auto-reducere
Despre creativitate
Matematica trivială
Criptografia inexistentă
Densitatea soluțiilor și dificultatea problemelor
Criptografia și NP-completitudinea
CNF-SAT și k-CNF-SAT
Tranziții de fază
http://www.nytimes.com/library/national/science/071399sci-satisfiability-problems.html.
Soluții practice pentru SAT
Algoritmul Putnam-Davis
Algoritmul Walk-SAT
Încheiere
Note