Eerste Toets Datastructuren 11 juli 2018, 13.30 15.30, Educ-α. Motiveer je antwoorden kort! Stel geen vragen over deze toets; als je een vraag niet duidelijk vindt, schrijf dan op hoe je de vraag interpreteert en beantwoord de vraag zoals je hem begrijpt. Cijfer: Vragen 2 en 6 tellen voor 2pt en de andere voor 3pt. Te halen 16pt, T1 is totaal plus 1 gedeeld door 1,5 (max 10). Maak vraag 1 en 2 op de voorkant, vraag 3 en 4 op pagina 2, en vraag 5 en 6 op pagina 3. 1. Binaire Derdemachtswortel: Dave schrijft een methode om de naar beneden afgeronde derdemachtswortel van een uint te berekenen. Hij gebruikt Binair Zoeken met invariant: i 3 x j > 3 x. Dave heeft deze code al getypt: uint DaveRoot(uint x) { uint i = 1; uint j = (1 << 31); while (j > i) (a) De tweede en derde regel bevatten een fout; welke en waarom is dat fout? (b) Schrijf de body voor Dave s loop (na verbeteringen uit vraag (a)). (c) Geef de return statement en een verbetertip voor dit programma. Oplossing: (a) De initialisatie van i op 1 voldoet niet aan de invariant voor legale invoer x = 0. Met conditie j > i kom je in een oneindige loop, dit moet zijn j > i + 1. (b) Midpoint, test, conditionele assignment: uint m = (i+j)/2; if (m*m*m <= x) i = m; else j = m; (c) return i; Na de lus is i 3 x i + 1 > 3 x dus het antwoord is i. Omdat 2 32 1 de maximale waarde van x is, kun je j initialiseren op 1626 (of op (1 << 11)); het gaat dan ongeveer driemaal zo snel. (Met beginwaarde 1636 voorkom je ook overflow.) Beoordeling/Toelichting: Per deelvraag een punt. Codes: C = Je kunt (j>i) gebruiken met een andere body, maar niet met Dave s invariant. D = De test (m <= 3 x) is niet goed, want het uitrekenen van de derdemachtswortel is juist het doel van Dave s methode. E = Early stopping is geen verbetering. H = Er werd gevraagd om de body, je hoefde niet de Hele methode te geven. K = De variant if (m*m*m < x)... is fout (want kan leiden tot een j met derdemacht x, wat de invariant verbiedt); min 1/2. L = Het berekenen van m*m*m kan overflow geven; je mag dit oplossen maar negeren geeft geen aftrek. Het kan bv met cast naar Long of initialisatie op een kleinere j. M = return m; is fout (zelfs als m buiten de body gedeclareerd is) en return j; ook. Het eerste geeft soms een foute waarde en het laatste altijd. T = Bereken m*m*m niet Tweemaal! U = De uint (1<<32) geeft waarde 0.
2. In Situ Sorteren: (a) Geef de definitie van een in situ sorteermethode. (b) Welke van deze methoden sorteren in situ? InsertionSort, QuickSort, MergeSort, Heap- Sort, BucketSort, CountingSort. (Zeg of je antwoord nog van extra aannames afhangt. Oplossing: (a) Behalve de geheugenruimte waar de te sorteren keys staan, is maar een geringe hoeveelheid extra geheugen nodig. (b) Insertion, Quick en Heap zijn in situ, MergeSort, BucketSort en CountingSort niet. Beoordeling/Toelichting: Voor (a) een punt en voor (b) een. Codes: C = CountingSort kan alleen zonder extra geheugen werken als er alleen keys zijn en geen satellietdata. Dat moet je er wel bij zeggen. G = Wat is Gering? Je kunt dit opvatten als Constant of als Logaritmisch. QuickSort gebruikt logaritmische ruimte op de call-stack! M = MergeSort kan wel een gelinkte lijst in situ sorteren, maar een array niet. P = Voor (b) 1pt als je 5 of 6 goed hebt, 1/2 als je 4 goed hebt. 3. Sommaties: Los op: (a) n i=0 (5 3i) (b) n 1 j=0 22j+1 (c) n i=1 (A i A n i ) Oplossing: (a) Pas de regel (E+L)A/2 voor rekenkundige reeksen toe: 1 (n + 1)(10 3n), 2 of 1 2 ( 3n2 + 7n + 10). (b) Meetkundige reeks, dus regel (V-E)/(G-1), met groeifactor 4: 1 3 (22n+1 2). (c) Hier komt A n A 0 uit. Je kunt dit inzien door het voor een kleine n even uit te schrijven, bv. n = 5 geeft (A 1 A 4 ) + (A 2 A 3 ) + (A 3 A 2 ) + (A 4 A 1 ) + (A 5 A 0 ), wat na wegvallen gewoon A 5 A 0 is. Of mooier, met echte wiskunde: n i=1 (A i A n i ) = n i=1 A i n i=1 A n i Termsplitsing = n i=1 A i n 1 i=0 A i Dummy Trans i wordt n i = n 1 i=1 A i + A n (A 0 + n 1 i=1 A i) Tweemaal afsplitsen = A n A 0 Wegvallende sommen Beoordeling/Toelichting: Per deelvraag een punt. Codes: A = Aantal bij (a) is n + 1. G = (b) lijkt een tweemacht maar door de 2i in de exponent is de Groeifactor hier 4. J = Vraag (b) vermeldde abusievelijk i in de exponent (antwoord is dan n 2 2i+1 wegens constante term), maar na tweemaal mondelinge correctie zijn hier gelukkig geen vergissingen mee gemaakt.
4. Orde van Sinus: (a) Geef de definitie van de uitspraak f(n) = O(g(n)). (b) Bewijs dat 3 + sin n = O(2 + cos n). Oplossing: (a) Er bestaan constanten c > 0, n 0, zo dat voor elke n n 0, f(n) c.g(n). (b) Gebruik dat een sinus max 1 is dus 3+sin is hoogstens 4, en een cos is minstens 1 dus 2+cos minstens 1. Hieruit haal je alvast je constante c = 4. Voor alle n 0 geldt: 3 + sin n 4 want sin n 1 4.(2 + cos n) want 1 cos n Beoordeling/Toelichting: Te halen 1pt voor (a) en 2pt voor (b). Codes: B = Een Bewijs is niet een redenering vanuit je stelling naar iets waars, maar vanuit iets waars (bv. gegevens) naar je stelling. N = Je definitie mist het stuk n 0, min 1/2pt.
5. Studentnummers sorteren: Je moet een programma schrijven waarmee dagelijks een log van aanmeldingen op de UU-netwerken wordt gesorteerd. Elke log-entry bestaat uit een studentnummer als int en een tekstje van ongeveer 100 karakters. Het aantal van een dag is ongeveer honderdduizend en het moet gesorteerd worden op studentnummer. Counting/RadixSort is hier een goede keuze, maar in hoeveel passes? (a) Hoeveel geheugen en tijd is ongeveer nodig wanneer je alleen Counting Sort gebruikt (dus 1 pass)? (b) Hoeveel geheugen en tijd is nodig als je twee passes gebruikt, eerst op de 16 minst significante bits en dan op de 16 meest significante? (c) Bespreek (kort!) een of twee mogelijke opties voor verdere verbetering. Oplossing: De omvang van de data is ca. 100.000 keer 100 bytes dus 10MB. Het lijkt zinvol, de hoeveelheid te gebruiken data in vergelijking hiermee te bekijken. In een ronde van CountingSort moet je n maal een counter ophogen, dan M optellingen doen voor de cumulatief, en n maal data verplaatsen. (a) Collegekaartnummers hebben 7 cijfers en lopen dus tot 10 miljoen. Je gebruikt een array van 10 miljoen counters (4B per stuk dus 40MB) en het cumuleren kost 10 miljoen stappen. Dat is veel meer dan de twee keer dat ik naar elk van de 100.000 keys kijk. In O-notatie, van de complexiteit O(n + M) is hier de M dominant. (b) Dit gebruikt tweemaal CountingSort met een M van 65536, waarvoor je dus 4x 65536 ofwel 1/4 MB nodig hebt voor de counters, en 65536 optellingen voor het cumuleren. Geheugengebruik en cumuleren zijn nu vrij klein ten opzichte van de tijd en het geheugen dat je toch al gebruikt. (c) Kortere passes: Collegekaartnummers zijn altijd kleiner dan 10 miljoen dus van de 32 bits worden er maar 24 gebruikt. Sorteren in twee passes op de 12 minst significante en dan de volgende 12 verkleint de overhead nog wat. Meer passes: Vier passes waarin je op acht bits sorteert verkleinen de M wel, waarmee de tijd van CountingSort iets afneemt, maar je verdubbelt het aantal handelingen voor elk van de 100.000 keys dus dit is niet slim. BucketSort: De spreiding van collegekaartnummers is niet gelijkmatig over het bereik. Te meer omdat sommige personen heel vaak inloggen. BucketSort is daarom geen goed idee. Insertion/Selection Sort: Kwadratische complexiteit, volkomen onaanvaardbaar. Quick/Merge/Heap Sort: Algemene methoden (comparison based) met Θ(n lg n) complexiteit, lijkt overbodig want Count/Radix-aanpak is veel sneller. Beoordeling/Toelichting: Een punt voor elke deelvraag. Het moet duidelijk zijn dat je begrijpt dat de M overhead bij (a) domineert en bij (b) juist klein is. Codes: B = Noemt BucketSort maar zegt niets over spreiding. Waarschijnlijk is bij een probleem als dit de spreiding slecht, maar als je dit aspect noemt (en zegt dat de spreiding goed is) is het al goed. C = Geheugen en tijd voor Counters (dus de extra Θ(M)) verwaarloosd. D = Elke log Direct invoegen kost je minstens logaritmische tijd per key dus is in totaal zeker niet beter dan twee passes CountingSort. E = Voor meerdere passes heb je maar Eenmaal de array met counters nodig omdat je die hergebruikt.
G = Geheugen 100000 keer 16b? De 16 MSB en 16 LSB sla je niet apart op. H = De Hoogte van de counters kan tot n oplopen. Tijdens het tellen al omdat je niets weet over de spreiding van de keys (in het meest extreme geval heeft 1 student 100000 keer ingelogd!). En bij het berekenen van de cumulatief lopen de waarden aan het eind van de tabel zeker op tot n. M = Meer passes verkleinen het geheugengebruik marginaal, maar laten wel de tijd toenemen, dus niet slim. R = Studentnummers zijn Redundant vanwege een checksum die maakt dat je alleen naar de eerste zes cijfers hoeft te kijken. Je kunt dus in een enkele pass met M = 1000000 sorteren op (nummer mod 10), of dit nog weer over twee passes M = 1000 verdelen. S = De Satellietdata (100byte strings) wordt niet echt verplaatst. Meestal sla je dit op in objecten en sorteer je de array met objecten, waardoor er alleen pointers echt worden verplaatst.. U = De keys zijn zeker niet Uniek (met 100000 logs van 30000 studenten). Je kunt ook niet aannemen dat elke student even vaak inlogt; waarschijnlijker is meerdere logins voor weinig studenten en geen voor heel veel studenten. V = Veranderen van dataopslag is het Veranderen van het probleem, niet het oplossen ervan. (In de praktijk zijn softwarebedrijven en wetenschappers hier trouwens erg succesvol mee.) W = Als je twee passes gebruikt in plaats van een, wordt de M niet de helft, maar de Wortel van de oude. Z = Iets noemen wat al in a of b Zit is geen verbetering. 6. Three-of-a-Kind: Hoe groot is de kans om, bij het nemen van 5 kaarten uit een spel van 52, een three-of-a-kind te krijgen? Three-of-a-kind betekent dat je van een van de kaartwaarden drie exemplaren hebt, bv drie boeren of drie achten. Vier boeren telt niet als three-of-a-kind. Van de andere twee kaarten maakt het niet uit of de kaartwaarde gelijk is; dwz. drie boeren en twee zevens is wel three-of-a-kind. ( ) 52 Oplossing: Elke combinatie van vijf kaarten is even waarschijnlijk, dus neem de = 5 2598960 combinaties als elementaire gebeurtenissen. Hoeveel combinaties tellen als ToaK? Er zijn dertien mogelijkheden voor de ( drievoudige ) 48 kaartwaarde, vier mogelijkheden voor de missende kaart van die waarde, en = 1128 2 mogelijkheden om het trio aan te vullen met twee kaarten van andere waarde. Dit geeft een kans van 13 4 1128 = 0, 02257. 2598960 Beoordeling/Toelichting: Twee pt voor een goed antwoord.