Progra-MEER - Leuven 21 februari 2018 Bart Demoen KU Leuven Veerle Fack UGent Frank Neven/Wim Lamotte UHasselt schooljaar 2017-2018
Vandaag... 2/35 huiswerk en rondvraag eem opwarmertje een oefening in specificatie en implementatie efficiëntie en complexiteit: sit back and relax minimale opspannende bomen en het algoritme van Prim uitleiding
Een opwarmertje... 3/35 zie werkblad
Een graaf modelleert de afhankelijkheden 4/35 we zoeken een topologische orde er zijn meerdere oplossingen
Algoritme voor topologisch sorteren 5/35 herhaal tot er geen knopen meer zijn kies een knoop zonder inkomende bogen output die knoop verwijder die knoop (en zijn uitgaande bogen) uit de graaf pas op: is er altijd nog een knoop zonder inkomende bogen?
Algoritme voor topologisch sorteren 5/35 herhaal tot er geen knopen meer zijn kies een knoop zonder inkomende bogen output die knoop verwijder die knoop (en zijn uitgaande bogen) uit de graaf pas op: is er altijd nog een knoop zonder inkomende bogen?
Hoe graaf getekend? 6/35 digraph g { rankdir=lr; size="8,5"; ka -> opd ; snw -> stw ; mav -> bins; stw -> opd ; ink -> mav; bav -> opd ; stw -> mav ; bins -> opd; ink -> snw ; saj -> snaj; snaj -> mav; snaj -> staj; sa -> ka; staj -> stw; ink -> sa; mav -> bav; ink -> saj; } webgraphviz.com
Opdracht: specifieer... 7/35 wanneer een rij (*) gesorteerd is in het nederlands en stel geen dubbels en laat je fantasie de vrije loop (*) lijst, array...
Een lijst is gesorteerd indien 8/35 voor elk element geldt: alle voorgangers zijn kleiner en alle opvolgers zijn groter voor elk element geldt: alle voorgangers zijn kleiner voor elk element geldt: alle opvolgers zijn groter de linkse helft is gesorteerd en de rechtse helft ook, en het meest rechtse element van de linkse helft is kleiner dan het meest linkse element van de rechtse helft voor elk element geldt: als het een opvolger heeft, dan is die groter wie heeft een andere? welke is de juiste? of de beste?... waarvoor kan je je specificatie gebruiken voor een implementatie?
voor elk element geldt: alle voorgangers zijn kleiner en alle opvolgers zijn groter 9/35 def gesorteerd1(l): for element in range(len(l)): for voorganger in range(0,element): if l[voorganger] > l[element]: return(false) for opvolger in range(element+1,len(l)): if l[opvolger] < l[element]: return(false) return(true)
de linkse helft is gesorteerd en de rechtse helft ook, en het meest rechtse element van de linkse helft is kleiner dan het meest linkse element van de rechtse helft 10/35 basisgeval vergeten? def gesorteerd2(l): if len(l) < 2: return(true) midden = len(l)//2 linksehelft = l[0:midden] if not(gesorteerd2(linksehelft)): return(false) rechtsehelft = l[midden:] if not(gesorteerd2(rechtsehelft)): return(false) return(l[midden-1] < l[midden])
voor elk element geldt: als het een opvolger heeft, dan is die groter 11/35 def gesorteerd3(l): for element in range(len(l)-1): volgende = element + 1 if l[element] > l[volgende]: return(false) return(true)
Wat hebben we geleerd? 12/35 een correcte specificatie leidt tot een correcte implementatie de implementaties lijken niet even efficiënt hoe meet je efficiëntie?
Hoe lang duurt optellen? 13/35 0 x 0 x x 0 x x x x x x x x x x x x + x x x + x x x + x x x... x x x x x x 0 x x x x + x x x x x x x x gegeven twee getallen (?) hoeveel elementaire operaties heb je nodig? altijd, of soms? duurt het langer een optelling te maken dan een optelling na te kijken?
Hoe geef je een getal aan een algoritme/implementatie? 14/35 eigenlijk geef je een representatie string: zevenentwintig decimaal: 27 of binair: 11011b of hexadecimaal: 1B x of romeins: XXVII of een uitleg: het atoomnummer van kobalt of unair: 1111111111111111111111111111 afhankelijk van de representatie heb je een ander algoritme nodig afhankelijk van de representatie kan het moeilijker of gemakkelijker zijn
Hoe geef je een getal aan een algoritme/implementatie? 14/35 eigenlijk geef je een representatie string: zevenentwintig decimaal: 27 of binair: 11011b of hexadecimaal: 1B x of romeins: XXVII of een uitleg: het atoomnummer van kobalt of unair: 1111111111111111111111111111 afhankelijk van de representatie heb je een ander algoritme nodig afhankelijk van de representatie kan het moeilijker of gemakkelijker zijn
Elementaire operaties... 15/35 optellen van 3 getallen kleiner dan 10 voor getallen tot 9: 1 operatie nodig voor getallen tot 99: 2 operaties nodig voor getallen tot 999: 3 operaties nodig voor getallen tot 9999: 4 operaties nodig in het algemeen N operaties als het getal N lang is - in binair, decimaal... het getal is dan tot bijna 2 N of 10 N... groot onderscheid tussen grootte getal en grootte voorstelling belangrijke fineprint: aantal stappen van het algoritme uitdrukken in functie van de grootte van de invoer, of de grootte van wat de invoer voorstelt (en bij welke representatie) één stap is één elementaire operatie = één kloktik
Elementaire operaties... 15/35 optellen van 3 getallen kleiner dan 10 voor getallen tot 9: 1 operatie nodig voor getallen tot 99: 2 operaties nodig voor getallen tot 999: 3 operaties nodig voor getallen tot 9999: 4 operaties nodig in het algemeen N operaties als het getal N lang is - in binair, decimaal... het getal is dan tot bijna 2 N of 10 N... groot onderscheid tussen grootte getal en grootte voorstelling belangrijke fineprint: aantal stappen van het algoritme uitdrukken in functie van de grootte van de invoer, of de grootte van wat de invoer voorstelt (en bij welke representatie) één stap is één elementaire operatie = één kloktik
Elementaire operaties... 15/35 optellen van 3 getallen kleiner dan 10 voor getallen tot 9: 1 operatie nodig voor getallen tot 99: 2 operaties nodig voor getallen tot 999: 3 operaties nodig voor getallen tot 9999: 4 operaties nodig in het algemeen N operaties als het getal N lang is - in binair, decimaal... het getal is dan tot bijna 2 N of 10 N... groot onderscheid tussen grootte getal en grootte voorstelling belangrijke fineprint: aantal stappen van het algoritme uitdrukken in functie van de grootte van de invoer, of de grootte van wat de invoer voorstelt (en bij welke representatie) één stap is één elementaire operatie = één kloktik
De complexiteit van de enkelvoudige lus 16/35 for i in range(0,n): x keer <iets elementairs> n keer de body van de lus dus: n x keer iets elementairs notatie: O(n) constante factor doet er niet toe... lineair in n
De complexiteit van de dubbele lus 17/35 for i in range(0,n): for j in range(0,n): x keer <iets elementairs> herschrijf naar for i in range(0,n-1): for j in range(0,n-1): x keer <iets elementairs> + i = n for j in range(0,n-1): x keer <iets elementairs> + for i in range(0,n): j = n x keer <iets elementairs>
De complexiteit van de dubbele lus 17/35 for i in range(0,n): for j in range(0,n): x keer <iets elementairs> herschrijf naar for i in range(0,n-1): for j in range(0,n-1): x keer <iets elementairs> + i = n for j in range(0,n-1): x keer <iets elementairs> + for i in range(0,n): j = n x keer <iets elementairs>
De complexiteit van de dubbele lus 18/35 for i in range(0,n): for j in range(0,n): x keer <iets elementairs> = for i in range(0,n-1): for j in range(0,n-1): x keer <iets elementairs> + i = n for j in range(0,n-1): x keer <iets elementairs> + for i in range(0,n): j = n x keer <iets elementairs> aantal(n) = aantal(n 1) + 2 n 1 zoals verwacht O(n 2 )
De complexiteit van de dubbele lus 18/35 for i in range(0,n): for j in range(0,n): x keer <iets elementairs> = for i in range(0,n-1): for j in range(0,n-1): x keer <iets elementairs> + i = n for j in range(0,n-1): x keer <iets elementairs> + for i in range(0,n): j = n x keer <iets elementairs> aantal(n) = aantal(n 1) + 2 n 1 zoals verwacht O(n 2 )
De complexiteit van lussen 19/35 heel dikwijls een vergelijking van de vorm aantal(n) = aantal(n 1) + O(N k ) oplossing is dan aantal(n) = O(N k+1 ) maar...
Binair zoeken 20/35 zoeken in een gesorteerde lijst def binzoek(x,l): if len(l) == 0: return(false) mid = len(l) // 2 if l[mid] == x: return(true) if l[mid] > x: return(binzoek(x,l[0:mid])) else: return(binzoek(x,l[(mid+1):])) aantal(n) = aantal( N 2 ) + c oplossing... aantal(n) = O(log(N))
Binair zoeken 20/35 zoeken in een gesorteerde lijst def binzoek(x,l): if len(l) == 0: return(false) mid = len(l) // 2 if l[mid] == x: return(true) if l[mid] > x: return(binzoek(x,l[0:mid])) else: return(binzoek(x,l[(mid+1):])) aantal(n) = aantal( N 2 ) + c oplossing... aantal(n) = O(log(N))
Merge-sort 21/35 in pseudo-python... mergesort(l): l1 = mergesort(eerstehelft(l)) l2 = mergesort(tweedehelft(l)) return(merge(l1,l2)) merge(l1,l2): if l1 == []: return(l2) if l2 == []: return(l1) if l1[0] < l2[0]: return [l1[0]] + merge(l1[1:],l2) else: return [l2[0]] + merge(l1,l2[1:]) aantal(n) = 2 aantal( N 2 ) + c N oplossing is aantal(n) = O(N log(n))
De kracht van 2 22/35 verdeel en heers complexiteit van O(N k ) naar O(N k 1 log(n)) een belangrijk en essentieel verschil N N 2 N log(n) verhouding 10 100 23 4 20 400 60 7 30 900 102 9 40 1600 148 11 50 2500 196 13 60 3600 246 15 70 4900 297 16 80 6400 351 18 90 8100 405 20 100 10000 461 22 1.000 1.000.000 6908 144 10.000 100.000.000 92103 1085
Is alles hoogstens kwadratisch of polynomiaal? 23/35 de theorie zegt: nee ( k:) veel problemen kunnen in O(n k ), maar niet in O(n k 1 ) veel problemen kunnen in O(2 n ), maar niet in O(n k ) ( k) problemen met een polynomiaal algoritme noemen we doenbaar (tractable) de andere ondoenbaar (intractable) waar ligt de scheidingslijn? theorie suggereert: tussen berekenen en nakijken
Is alles hoogstens kwadratisch of polynomiaal? 23/35 de theorie zegt: nee ( k:) veel problemen kunnen in O(n k ), maar niet in O(n k 1 ) veel problemen kunnen in O(2 n ), maar niet in O(n k ) ( k) problemen met een polynomiaal algoritme noemen we doenbaar (tractable) de andere ondoenbaar (intractable) waar ligt de scheidingslijn? theorie suggereert: tussen berekenen en nakijken
Berekenen of nakijken, wat is moeilijker? 24/35 onze intuïtie wordt gesteund door de ervaring... een deler van N berekenen is moeilijker dan nakijken of een gegeven getal een deler is van N een bewijs maken is moeilijker dan een bewijs nakijken nakijken dat een rij gesorteerd is, is gemakkelijker dan een rij te sorteren een pad van A naar Z berekenen is moeilijker dan nakijken of een gegeven pad van A naar Z loopt een fuga componeren is moeilijker dan nakijken of een gegeven muziekstuk een fuga is een hattrick maken is moeilijker dan na te kijken of een hattrick gemaakt werd... heb je zelf voorbeelden?
P en NP 25/35 ruwweg: P zijn de problemen waarvoor een polynoom berekenalgoritme bestaat NP zijn de problemen waarvoor een polynoom nakijkalgoritme bestaat P NP de hamvraag: P? = NP
De moeilijkste problemen in NP: NP-compleet 26/35 kleuren van grafen Hamiltoniaanse kring (pad) kliek knapzak het feestprobleem langste weg tussen twee knopen (contrast met Dijkstra) minimale Steiner boom (contrast met MOB) Sudoku(N) meest bekende en eerste (Cook 1971, Levin 1973): SAT heeft een propositionele formule zoals (p q r) & (s t p) &... een toekenning die de formule waar maakt?
De moeilijkste problemen in NP: NP-compleet 26/35 kleuren van grafen Hamiltoniaanse kring (pad) kliek knapzak het feestprobleem langste weg tussen twee knopen (contrast met Dijkstra) minimale Steiner boom (contrast met MOB) Sudoku(N) meest bekende en eerste (Cook 1971, Levin 1973): SAT heeft een propositionele formule zoals (p q r) & (s t p) &... een toekenning die de formule waar maakt?
NP-complete problemen 27/35 gemakkelijk om na te kijken of iets een oplossing is is dit een 3-kleuring voor deze graaf? moeilijk om ervoor een oplossing te berekenen bestaat een 3-kleuring voor deze graaf? gemakkelijk betekent: polynomiaal (we kennen het algo) moeilijk betekent: er zijn enkel exponentiële algoritmen bekend, geen polynomiale los P? = NP op en win 1.000.000 US$ :-)
NP-complete problemen 27/35 gemakkelijk om na te kijken of iets een oplossing is is dit een 3-kleuring voor deze graaf? moeilijk om ervoor een oplossing te berekenen bestaat een 3-kleuring voor deze graaf? gemakkelijk betekent: polynomiaal (we kennen het algo) moeilijk betekent: er zijn enkel exponentiële algoritmen bekend, geen polynomiale los P? = NP op en win 1.000.000 US$ :-)
NP-complete problemen 27/35 gemakkelijk om na te kijken of iets een oplossing is is dit een 3-kleuring voor deze graaf? moeilijk om ervoor een oplossing te berekenen bestaat een 3-kleuring voor deze graaf? gemakkelijk betekent: polynomiaal (we kennen het algo) moeilijk betekent: er zijn enkel exponentiële algoritmen bekend, geen polynomiale los P? = NP op en win 1.000.000 US$ :-)
Uitwiskeling... 28/35 Kürt Maes
Structuur van eindigheidsbewijs 29/35 iets wordt steeds groter en kan niet boven een bepaalde waarde uitstijgen iets wordt steeds kleiner en kan niet onder een bepaalde waarde dalen iets is dikwijls een variabele in het programma of algoritme kan ook een afgeleide grootheid zijn - bv. de som van de graden van de knopen o je bewijst daarmee dat een herhalingsopdracht slechts een eindig aantal keer uitgevoerd wordt o als je exact weet hoeveel keer (of de O()), dan kan je de O() van het programma afleiden
De Collatz-functie... eindigt die? 30/35 def collatz(n): while n!= 1: if even(n): n = n//2 # n wordt kleiner :-) else: n = 3*n+1 # n wordt groter :-( return("gedaan") of de recursieve versie
De Collatz-functie... eindigt die? 31/35 def collatz(n): if n == 1: return( gedaan ) if even(n): n = n//2 # n wordt kleiner :-) else: n = 3*n+1 # n wordt groter :-( return(collatz(n)) Pal Erdös mathematics is not yet ready for such problems en ik voeg eraan toe: informatica is nog niet toe aan dit soort problemen :-)
De Collatz-functie... eindigt die? 31/35 def collatz(n): if n == 1: return( gedaan ) if even(n): n = n//2 # n wordt kleiner :-) else: n = 3*n+1 # n wordt groter :-( return(collatz(n)) Pal Erdös mathematics is not yet ready for such problems en ik voeg eraan toe: informatica is nog niet toe aan dit soort problemen :-)
De Collatz-functie... eindigt die? 31/35 def collatz(n): if n == 1: return( gedaan ) if even(n): n = n//2 # n wordt kleiner :-) else: n = 3*n+1 # n wordt groter :-( return(collatz(n)) Pal Erdös mathematics is not yet ready for such problems en ik voeg eraan toe: informatica is nog niet toe aan dit soort problemen :-)
Correctheidsbewijs voor herhalingsopdracht 32/35 een bepaalde uitspraak U is waar bij het binnenkomen van de herhalingsopdracht als U waar is bij het begin van de body, dan ook op het einde nadat de herhalingsopdracht stopt, is U waar en ook de stopconditie U wordt een programma-invariant genoemd
Klein voorbeeld van een correctheidsbewijs 33/35 vermenigvuldigen door herhaald optellen lees(x); lees(y) n,m = x,y res = 0 x*y == res + n*m while m > 0: x*y == res + n*m [== res+n + n*(m-1)] res += n m-- x*y == res + n*m x*y == res + n*m & m == 0 # dus x*y = res print(res)
Correctheidsbewijs voor een functie f die g oproept 34/35 def f():... x = g()... return(...) bewijs (of onderstel) dat g correct is maak een invariant voor g gebruik die in het bewijs van correctheid voor f
Correctheidsbewijs voor een recursieve functie 35/35 def f(n): if N == 17: return(...) x = f(n-1)... return(...) # basisgeval de functie is correct voor input == het basisgeval stel dat de functie correct is voor alle gevallen strikt kleiner dan N, bewijs dan dat de functie correct is voor N vaak verschillende bewijstechnieken nodig