8. Complexiteit van algoritmen:

advertisement
8. Complexiteit van algoritmen:
_________________________________________
Voorbeeld: Een gevaarlijk spel ________________________________ 1
Spelboom voor het wespenspel ________________________________ 2
8.1
8.2
8.3
8.4
Complexiteit ______________________________________4
NP-problemen ____________________________________6
De oplossing _____________________________________7
Een vuistregel ____________________________________8
In dit hoofdstuk wordt het duidelijk dat er problemen zijn
waarvoor er geen volledig correct antwoord te berekenen is.
Met een voorbeeld wordt duidelijk gemaakt dat het
verstandig is om eerst de orde van grootte van je probleem te
analyseren en pas daarna het algoritme te bedenken. Je
maakt kennis met NP-problemen en NP-volledige problemen
en hun oplossingen.
Voorbeeld: Een gevaarlijk spel
In het volgende voorbeeld gaan we een spel spelen met twee sluipwespen. De
sluipwespen worden om de beurt in een bakje met twee drosophila poppen
gezet. Er wordt gewacht totdat de wesp in het bakje twee eitjes gelegd heeft.
Wanneer de wesp haar 2 eitjes gelegd heeft, is de beurt aan de andere sluip-
wesp. Deze mag ook 2 eitjes leggen. Dit spel gaat door totdat in beide
drosophila poppen 5 eitjes gelegd zijn.We nemen aan dat de wespen kunnen
waarnemen hoeveel eitjes van haarzelf en hoeveel eitjes van een andere wesp
in de pop aanwezig zijn. De wesp die het meeste eitjes in een pop heeft, heeft
de grootste kans dat de uiteindelijke wesp die uit de pop ontstaat een kind van
Pagina 8 - 2
haar is en heeft een punt gewonnen. Bij 2 poppen kan een wesp dus met 2-0
winnen of 1-1 gelijk spelen.
Stel dat de wespen de namen w1 en w2 krijgen en de drosophila poppen de
namen pop1 en pop2. W1 mag met het spel beginnen.
Er zou dan een volgend spelverloop kunnen zijn:
pop1
pop2
w1
w2
w1
w2
w1 legt 2 eitjes in pop1
2
0
0
0
w2 legt 1 eitje in pop1 en 1 eitje in pop2
2
1
0
1
w1 legt 1 eitje in pop1 en 1 eitje in pop2
3
1
1
1
w2 legt 2 eitjes in pop2
3
1
1
3
w1 legt 1 eitje in pop1 en 1 eitje in pop2
4
1
2
3
Allebei de poppen zitten nu vol, w1 heeft pop1 gewonnen en w2 heeft pop2
gewonnen de eindstand is dus 1-1.
De 5 rijen met cijfers geven de toestand aan waar het spel zich op dat moment
in bevindt. Hier zijn slechts 5 toestanden te zien,in totaal zijn er echter veel
meer. Om een overzicht te krijgen van het aantal toestanden, is het handig om
een spelboom te maken.
Hieronder zie je een voorbeeld van zo'n spelboom.
00 00
Wesp 1
20 00
10 10
Wesp 2
22 00
20 02
21 01
02 20
00 22
01 21
12 10
10 12
Wesp 1
22 20
32 10
Wesp 1
Wesp 2
32 30
23 21
22 22
32 32
Wesp 1 wint
23 41
Gelijkspel
32 32
Wesp 1 wint
Spelboom voor het wespenspel
Door ruimte gebrek is deze spelboom niet helemaal afgemaakt. De toestanden
zijn de knopen van de boom en de getallen die erbij staan (00 00, 20 00 enz.)
zijn weergaven van de stand (net zoals in de tabel). Als je vanuit een knoop
naar links gaat dan leg je in pop1 2 eitjes, als je vanuit een knoop de middelste
tak neemt dan leg je in pop2 2 eitjes,als je de rechter tak neemt dan legt de
wesp 1 eitje in pop1 en 1 eitje in pop2.
Er zijn ook knopen die illegale toestanden afbeelden. Dit zijn toestanden
die niet voor mogen komen. In zo’n geval zouden er meer dan 5 eitjes in een
pop gelegd worden. (Zoek ze op en geef ze met een kruis aan).
Hoofdstuk 8
11 11
Pagina 8 - 3
Wanneer je nu wilt weten welke zet je bij een bepaalde toestand moet doen, kan
je voor elke mogelijke zet door de spelboom naar beneden wandelen en kijken
wat de kans op winst is bij die bepaalde zet.Welke zet zou jij b.v. doen wanneer
je wesp1 was en de toestand is 22 00?
Bij 2 poppen is deze boom nog redelijk te overzien. Het spel kan echter ook met
4 of 5 poppen gespeeld worden. De spelboom zal in die gevallen groter worden.
De vraag is echter: Hoe groot worden die bomen?
De diepte van de boom is gelijk aan het aantal eieren gedeeld door 2.
Als er 2 poppen zijn dan moeten er 10 eieren gelegd worden. Omdat er per
beurt 2 eieren gelegd worden, zijn er 10/2 = 5 beurten nodig voordat de poppen
vol zijn. De diepte van de spelboom is dus 5.
In het algemeen kan je zeggen dat voor n poppen de diepte 5n/2 = 2.5n zal
zijn.
Het aantal mogelijkheden waarop de wesp haar n eieren kan leggen neemt ook
bij het toenemen van het aantal toe.
Bij 1 pop kan het maar op 1 manier.
Bij 2 poppen kan het op 1 + 2 = 3 manieren.
Bij 3 poppen kan het op 1 + 2+ 3 = 6 manieren
Bij n poppen kan het op 1 + 2 + 3......+ n manieren.
Wanneer je alle punten (alle toestanden) wilt berekenen moet je voor elk niveau
berekenen hoeveel toestanden er per niveau zijn.
Bij n = 2 zijn er op het eerste niveau 3 toestanden, op het tweede niveau zijn er
9, op het derde niveau zijn er 27, op het vierde niveau zijn er 81 en op het
vijfde niveau zijn er 243 toestanden; in totaal zijn er dus 363 toestanden (voor
het gemak zijn de onmogelijke toestanden meegerekend, het gaat namelijk om
de orde van grootte).
In het algemeen geldt:
∑(1+2+…+n)n voor n=1 tot 2.5a
Waarbij a het aantal poppen voorstelt.
Voor a = 2 geldt dus:
∑(1+2)n voor n=1 tot 5
Dit is 3 + .... + 32 + 33 +34 + 35 = 363 toestanden
Als we voor een aantal poppen het aantal toestanden berekenen dan komen we
tot de volgende tabel:
aantal poppen
aantal toestanden
tijdsduur
1
3
0.03 sec
2
363
3.63 sec
3
335922
56 minuten
4
1.11 x 10 10
3.5 jaar
5
2 x 1015
661215 jaar
Bij tijdsduur hebben we aangenomen dat de computer ongeveer 0.01 seconde
nodig heeft om een toestand te berekenen.
Hoofdstuk 8
Pagina 8 - 4
8.1 Complexiteit
Je ziet dat de tijd die nodig is schrikbarend toeneemt met het
aantal poppen. Bij 5 poppen is de oplossing voor het probleem
al onuitvoerbaar (ook al zou je een duizend keer zo snelle computer hebben).
We spreken in zo’n geval van een onhandelbaar probleem.
Voor dergelijke problemen is geen redelijk algoritme
beschikbaar. Wat verstaan we dan onder een redelijk
algoritme? Om te kunnen beoordelen of er voor een probleem
een redelijk algoritme mogelijk is moeten we kijken naar de
samenhang tussen de omvang van de invoer van een probleem
en de hoeveelheid tijd en geheugenruimte die nodig is om
een oplossing van het probleem te berekenen. Die twee
factoren bepalen de complexiteit van een algoritme.
Complexe algoritmen vragen veel van een computer in termen
van beschikbaar geheugen en vereiste rekentijd door de
centrale processor (CPU).
Complexiteit kunnen we beschrijven door te kijken naar het
aantal handelingen en/of vergelijkingen dat nodig is om van
input naar gewenste output te komen. Daarbij gaat het niet om
het precieze aantal handelingen maar om de orde van grootte
van het aantal handelingen. Die orde van grootte geven we aan
met de hoofdletter O. We spreken daaarom ook wel van de
grote-O-notatie. Wanneer we van een probleem zeggen dat het
in de klasse O(n) ligt dan betekent dat dat er een lineair
verband is tussen de omvang van het probleem, i.e., het aantal
te verrichten handelingen of vergelijkingen dat nodig is om de
input om te zetten in een oplossing van het probleem, en de
hoeveelheid tijd die daar voor nodig is. We onderscheiden
verschillende klassen van problemen al naar gelang de relatie
tussen omvang en tijd:
O(log n)
: logaritmisch
O(n)
: lineair
O(n2)
: kwadratisch (i.h.a., polynomiaal)
O(2n )
: exponentieel
Problemen die tot de eerste drie klassen behoren noemen we
handelbaar. Voor dergelijke problemen zijn redelijke
algoritmen mogelijk. Zodra de relatie tussen omvang van het
probleem en de benodigde rekentijd exponentieel van karakter
wordt noemen we het probleem onhandelbaar, en is er geen
redelijk algoritme mogelijk. In het algemeen geldt dus dat het
asymptotisch gedrag (hoe snel verandert de rekentijd met
Hoofdstuk 8
Pagina 8 - 5
de grootte van het probleem) bepaalt of een probleem uitvoerbaar is of niet.
Een voorbeeld van een handelbaar probleem is bijvoorbeeld
het zogenaamd lineair zoeken in een lijst (met namen
bijvoorbeeld). Van de lijst is bekend dat er N namen in staan.
Het is onbekend of de lijst is geordend, d.w.z. we weten niet
van te voren of de namen alfabetisch zijn gerangschikt. We
zoeken naar de naam X en we weten zeker dat de naam in de
lijst voorkomt. De vraag is nu: hoe complex is dit probleem ?
Om die vraag te kunnen beantwoorden moeten we ons een
idee vormen over hoeveel namen uit de lijst we moeten
vergelijken met de naam die we zoeken. We weten dat als de
gezochte naam X als eerste op de lijst voorkomt er maar 1
vergelijking nodig is geweest. Staat X op plaats 2 dan zijn er 2
vergelijkingen nodig, en staat X op plaats N dan zijn er N
vergelijkingen nodig.
Dat wil zeggen dat voor dit probleem in het algemene geval
voor het aantal te verrichten handelingen geldt:
(1+2+3+…+N)/N =
0.5N (N+1)/N =
0.5 (N+1) =
O(N)
De orde van grootte van het lineaire zoek probleem is dus
recht evenredig met de omvang van de lijst waarin we moeten
zoeken. We mogen dus verwachten dat er een redelijk
algoritme bestaat om dit zoek-probleem op te lossen.
Het is zelfs zo dat er een beter algoritme te formuleren is als
we iets aan de eigenschappen van de lijst kunnen sleutelen.
Een beter algoritme wil in dit geval zeggen dat het een
gunstiger relatie tussen probleem-omvang en benodige
rekentijd bezit. Om dit te kunnen bereiken is het nodig dat de
lijst is gesorteerd, d.w.z. alfabetisch is geordend (om zover te
komen met die lijst is op zich zelf natuurlijk ook weer een
probleem, een zg. sorteer probleem, waar in de literatuur zeer
veel oplossingen voor worden aangereikt).
We hebben dus weer een lijst L met N namen waarin we op
zoek zijn naar naam X. L is alfabetisch geordend, en X komt
voor in L. Hoe complex is dit probleem, m.a.w. hoeveel
vergelijkingen zijn er globaal nodig om X te vinden ?
Herhaal
M := middelste element uit L
Hoofdstuk 8
Pagina 8 - 6
Als
X KLEINER DAN M
Dan
L := bovenste helft van L
Anders
L := onderste helft van L
Eindals
Totdat
X IS_GELIJK M
Eindherhaal
Stel dat N = 2k - 1, en k = 3. N is dan gelijk aan 7
Als X gelijk is aan het middelste element (nummer 4) dan is er
1 vergelijking nodig geweest. Indien dat niet het geval is wordt
L gelijk aan of de bovenste of de onderste helft van L (met nog
maar drie namen). Indien X gelijk is aan de middelste naam
(de tweede) dan zijn er 2 vergelijkingen nodig geweest. Indien
niet, dan breken we L weer op in of de bovenste of de onderste
helft. Hier zit dan nog maar 1 naam in die per definitie (want
we hebben afgesproken dat X in de lijst zit) gelijk moet zijn
aan de gezochte naam.
Dat wil zeggen dat voor dit probleem in het algemene geval
voor het aantal te verrichten handelingen geldt:
(1x1 + 2x2 + 4x3 + … + (2k-1 x k))/N =
((k-1)x 2k+1)/N =
(N+1) x ((2log(N+1)-1)/N) + 1/N =
log (N+1) - 1 =
2
O(logN)
De logaritme van N is duidelijker kleiner dan N zelf. We
hebben om dit probleem op te lossen bij een gelijke omvang
van de lijsten dus minder rekentijd nodig dan met het lineaire
zoek probleem.
8.2 NP-problemen
In het algemeen geldt dus dat de tijd die nodig is om een
probleem op te lossen een functie is van de omvang van het
probleem. Bij een probleem dat in polynomiale tijd uitvoerbaar is, een P-probleem, hoort een functie waarbij n (aantal)
het grondtal is, b.v. f(n) = n of f(n) = n + n 2. Bij een probleem
dat niet in een polynomiale tijd te berekenen is, hoort een
Hoofdstuk 8
Pagina 8 - 7
functie waarbij n in de macht staat, b.v. f(n) = en of f(n) = n +
42n .
Een bijzondere klasse van problemen wordt gevormd door
gevallen waarvan de status onbekend is. Er bestaan wel algoritmen voor de afzonderlijke gevallen uit deze categorie, maar
deze oplossingen zijn geen van alle redelijk. Maar van deze
gevallen is daarentegen ook nog niet bewezen dat ze tot de
categorie van onhandelbare problemen behoren. Ze zijn in
polynomiale tijd te berekenen als we zouden kunnen raden
(in plaats van van te voren exact bepalen = determineren) hoe
de berekening moet gaan. We noemen een probleem wat tot
deze categorie behoort een NP-probleem (NP = Nondeterministisch Polynomiaal).
Voorbeelden van NP problemen zijn:
Het berekenen van de beste zet bij schaken
een lesrooster maken;
het bepalen van de kortste weg, als je een gegeven aantal plaatsen moet
bezoeken.
8.3 De oplossing
Toch willen we voor de NP problemen een oplossing hebben.
Dit kan, maar dan moeten we met iets minder tevreden zijn. In
veel gevallen kan er namelijk een vuistregel, of een andere truc
bedacht worden die [vaak] een goed antwoord geeft. We
spreken in zo’n geval van een heuristische (heuriskein –
grieks voor ‘ontdekken’) in plaats van een exacte oplossingsmethode. We noemen een methode heuristisch als het op
basis van ervaring of logisch redeneren redelijk aannemelijk te
maken is dat die methode een goede oplossing voor een
probleem oplevert, echter zonder dat we kunnen garanderen
dat die oplossing optimaal is.
Er zijn talrijke voorbeelden van dergelijke op ervaring
berustende vuistregels. Voor de 3 problemen die we boven
noemden zouden we met de volgende compromissen tevreden
kunen zijn:
Bij schaken wordt maar een aantal zetten vooruit
bedacht en worden standaard regels gebruikt.
Bij lesroosters wordt een rooster gemaakt waar iedereen
mee kan leven, het bevat dan nog tussen-uren en lange
dagen.
Hoofdstuk 8
Pagina 8 - 8
Bij het vinden van de kortste weg worden de eisen iets
verzwakt en wordt gezocht naar een acceptabel korte
weg.
8.4 Een vuistregel
Even terug naar het
probleem waar we dit
hoofstuk mee begonnen.
Bij onze voorstelling van
de eileg competitie tussen
twee wespen als spel, zou
de beste zet berekend
kunnen worden door het
uitrekenen van de komplete spelboom. Bij 5 poppen zou het
berekenen van de beste zet dan 661215 jaar in beslag nemen.
We kunnen echter ook een vuistregel verzinnen die in de
meeste gevallen wel voor winst zorgt.
Een voorbeeld van een vuistregel is:
1.
2.
3.
4.
5.
6.
7.
Zoek naar poppen met 4 eitjes waarvan er 2 van jezelf
zijn en leg er 1 eitje bij.
Zoek naar poppen met 3 eitjes waarvan er 1 van jezelf is
en leg er 2 eitjes bij.
Zoek naar poppen met 1 eitje en leg er 1 eitje bij.
Zoek naar een lege pop en leg alle overige eieren.
Leg 1 eitje bij poppen met 2 eigen eieren.
Leg 1 eitje bij poppen met 2 andere eieren.
Leg willekeurig ergens een eitje.
Voor alle duidelijkheid: Dit is één vuistregel die uit 7 regels
bestaat welke in volgorde van belangrijkheid gerangschikt zijn.
Misschien is er een betere vuistregel te vinden, maar deze
vuistregel zal het in de praktijk aardig doen.
Het grote voordeel van deze vuistregel is de tijdswinst.
Bij 5 poppen zijn er 13 speelbeurten.
13 keer moeten er dus (maximaal) 7 regels over 5 poppen toegepast worden.
Er moeten dus 13x7x5 = 455 handelingen verricht worden. Stel
dat ook dit keer elke handeling 0.01 seconde duurt, dan zou
het spel in 4.55 seconden gespeeld zijn.
Hoofdstuk 8
Pagina 8 - 9
Een ander groot voordeel is de hoeveelheid geheugen die de
computer aan moet spreken om het probleem op te lossen. Bij
de vuistregel levert dit geen enkel probleem op, terwijl bij het
berekenen van een hele spelboom er waarschijnlijk een
geheugen probleem ontstaat.
Hoofdstuk 8
Pagina 8 - 10
Hoofdstuk 8
Download