# NP-volledig

NP-volledigheid is een concept uit de complexiteitstheorie. Het is een beschrijving van het inzicht uit de jaren 70 dat er een bepaald verband is tussen de complexiteit van een groot aantal problemen die in de wiskunde en informatica als "moeilijk" worden beschouwd.
In formele zin is een probleem NP-volledig (ook soms NP-compleet genoemd) als en slechts als
* het probleem tot de complexiteitsklasse NP behoort.
* elk ander probleem in NP in polynomiale tijd naar dit probleem kan worden gereduceerd.

## De basis

De complexiteitstheorie is de tak van de wiskunde en de informatica die bestudeert of problemen oplosbaar zijn of niet en als ze wel oplosbaar zijn, hoe moeilijk ze dan zijn om op te lossen. Het voornaamste hulpmiddel bij dit onderzoek is een conceptueel model van berekening, genaamd de turingmachine. Dit model beschrijft op een zeer laag niveau en op zeer mechanische wijze hoe een berekening uitgevoerd wordt. Een dergelijke beschrijving bestaat feitelijk uit een opsomming van alle stappen die doorlopen moeten worden om van een probleemstelling tot een oplossing te komen. Door gebruik te maken van dit model om berekeningen te beschrijven, wordt het mogelijk om te bepalen of een gegeven probleem überhaupt wel oplosbaar is. Een probleem wordt beschouwd als oplosbaar wanneer er een turingmachine kan bestaan die het probleem beslist.
Wanneer van een probleem vaststaat dat het oplosbaar is, dient zich de vraag aan hoe moeilijk het probleem is om op te lossen. Om hier een antwoord op te geven, wordt gekeken naar de complexiteitsgraad van het probleem.

## Klassen van oplosbaarheid

De verzameling van turingbeslisbare (dat wil zeggen, de oplosbare) problemen bevat twee klassen: de klasse P van "makkelijk" op te lossen problemen en de klasse NP van "makkelijk" te verifiëren problemen. Het is een open vraag of de klassen samenvallen of niet.

### De klasse P

Over het algemeen worden die (oplosbare) problemen als "makkelijk" beschouwd, die een polynomiale tijdscomplexiteit hebben. Dat wil zeggen:
    $P=\{A\mid A={\mathcal {O}}(f(x))\wedge f(x)=\sum _{k=0}^{N}c_{k}x^{k}\}$
De x in bovenstaande formule staat voor de grootte van de probleeminstantie, oftewel de grootte van de invoer voor de turingmachine.
Anders gezegd: de klasse P bevat de problemen die opgelost kunnen worden met een algoritme dat in polynomiale tijd uitgevoerd kan worden (de tijdsduur van het algoritme wordt begrensd door een polynoom).

### De klasse NP

Het wordt vaak gedacht dat NP voor 'niet-polynomiaal' staat, en dat de klasse NP dus uit de problemen bestaat die niet in polynomiale tijd op te lossen zijn, maar dit is incorrect. NP staat voor 'niet-deterministisch polynomiaal' en bevat de problemen die in polynomiale tijd kunnen worden opgelost op een niet-deterministische turingmachine.
Een niet-deterministische turingmachine is een denkbeeldige machine die verschillende varianten van een berekening tegelijk kan onderzoeken. De machine kiest automatisch voor de variant waarin een oplossing gevonden wordt, en verwerpt de varianten die niet tot een oplossing leiden. Het is echter onbekend of zo'n machine in werkelijkheid kan bestaan.
Het is bekend dat de klasse P een deelverzameling is van de klasse NP ($P\subseteq {NP}$). Immers, P-problemen kunnen in polynomiale tijd opgelost worden en dus per definitie ook in niet-deterministisch polynomiale tijd. Of met andere woorden: als je een probleem in polynomiale tijd kunt oplossen, kun je het ook verifiëren in polynomiale tijd.
Er zijn echter ook problemen in NP waarvan veel hedendaagse wiskundigen geloven dat ze niet in deterministisch-polynomiale tijd oplosbaar zijn (en dus "moeilijk" zijn). Voorbeelden daarvan zijn de NP-volledige problemen.
De problemen in NP worden allemaal gekarakteriseerd door een aparte eigenschap: ze zijn polynomiaal verifieerbaar. Dat wil zeggen dat we deze problemen misschien niet polynomiaal op kunnen lossen, maar als we voor een probleem een oplossing vermoeden dan kunnen we wel in polynomiale tijd nagaan of die oplossing klopt.
Anders gezegd: de klasse NP bevat alle problemen waarvoor een algoritme bestaat dat, gegeven een voorgestelde oplossing, in polynomiale tijd kan controleren of de voorgestelde oplossing correct is.

## Reductie en NP-volledigheid
### Reductie

In de complexiteitstheorie bestond al langer het besef dat je eigenschappen van bekende problemen over kon hevelen naar nieuwe problemen als je aan kon tonen dat je het nieuwe probleem uit kon drukken in termen van het oude probleem; dit heet het reduceren van een probleem. Een typisch voorbeeld hiervan is het probleem van het vinden van een Euler-pad, dat uitgedrukt kan worden in termen van het vinden van een Euler-cykel. Of het vinden van een Hamilton-cykel door middel van het handelsreizigersprobleem.
Aan het begin van de jaren 1970 kwamen twee wiskundigen -- Stephen Cook en Leonid Levin -- tot een bijzonder inzicht. Binnen de klasse NP bestaat een stelsel van problemen die aan elkaar gerelateerd zijn via reductie in polynomiale tijd (dat wil zeggen, het omschrijven van het ene probleem in het andere kan in polynomiale tijd). Sterker nog, dit stelsel van problemen is gerelateerd aan alle problemen in NP door middel van reductie in polynomiale tijd.
Binnen NP bestaat een deelverzameling van NP. Ieder probleem in NP kan opgelost worden in termen van al deze, bijzondere problemen (het is dus ook mogelijk om deze problemen onderling tot elkaar reduceren). Cook en Levin noemden deze, bijzondere deelverzameling van NP de verzameling NPC (van het Engels NP-Complete): de verzameling van NP-volledige problemen.

### NP-volledigheid

NP-volledige problemen zijn problemen die in de complexiteitsklasse NP liggen en waarvoor bovendien geldt, dat ieder probleem in NP in polynomiale tijd ertoe gereduceerd kan worden. Ze kunnen dus ook onderling tot elkaar gereduceerd worden. Daarmee vormen de NP-volledige problemen een complete graaf van onderlinge reduceerbaarheid. De introductie van NP-volledigheid in de wiskunde en informatica was een belangrijk hulpmiddel bij de classificatie van problemen. Het is mogelijk ook de sleutel tot de oplossing van het bovenstaande mysterie. Immers, ieder probleem in NP is reduceerbaar tot ieder NP-volledig probleem. Wordt er dus ooit een NP-volledig probleem gevonden dat polynomiaal oplosbaar is, dan volgt automatisch dat alle NP problemen polynomiaal oplosbaar zijn, oftewel: $P=NP$.
Het vervulbaarheidsprobleem is het eerste probleem waarvoor NP-volledigheid werd aangetoond.

### Bewijs van NP-volledigheid

Om te bewijzen dat een gegeven probleem A NP-volledig is, zijn formeel twee dingen nodig:
* Een bewijs dat A behoort tot NP
* Een bewijs dat ieder probleem in NP in polynomiale tijd tot A reduceert
Het eerste gedeelte is vaak (maar niet altijd!) eenvoudig door een algoritme te geven dat oplossingen voor A in polynomiale tijd verifieert. De uitdaging zit meestal in het tweede stuk, het bewijs dat ieder probleem in NP tot A reduceert. Problemen die aan de tweede eis voldoen worden ook wel NP-moeilijk genoemd. Het is echter ook mogelijk dat een probleem wel aan de tweede eis, maar niet aan de eerste voldoet, bijvoorbeeld de beste zet in een schaakspel kiezen, dit is ‘moeilijker’ dan NP. Dit bewijs wordt echter meestal vereenvoudigd tot een bewijs dat een of ander probleem in NPC tot A reduceert. Dit is een uitbreiding van de "NPC-boom" en dat volstaat als bewijs. Stel namelijk dat B een probleem in NPC is. Dan reduceert ieder probleem in NP (inclusief A) tot B. Als B ook tot A reduceert, dan reduceert ieder probleem in NP tot A en wel via B. Het begin van de "NPC-boom" wordt gevormd door het probleem SAT (satisfiability). Cook en Levin toonden, onafhankelijk van elkaar, de NP-volledigheid van dit probleem aan met een uitzonderlijk bewijs (waarin zij uiteraard niet het gemak hadden van het reduceren van een ander NP-volledig probleem, want hun probleem was het eerste waarvan de NP-volledigheid bekend was).
* Introduction to the theory of computation Michael Sipser PWS Publishing Company, 1996 ISBN 0-534-94728-X