Innehåll Föreläsning Mängd, lexikon och hashtabell Mängd Lexikon Hashtabell Mängd Specifikation Modell: En påse, men den är inte riktigt bra eftersom man tex kan ha mängder med gemensamma element. Organisation: En oordnad samling av element som är av samma typ. Grundmängden behöver inte vara ändlig (heltal) men dataobjekten är ändliga. Kan inte innehålla två likadana värden. En mängd kan inte innehålla mängder. abstract datatype Set(val) Empty()->Set(val) Single(v:val)->Set(val) Insert(v:val,s:Set(val))->Set(val) Union(s:Set(val),t:Set(val))->Set(val) Intersection(s:Set(val),t:Set(val))->Set(val) Difference(s:Set(val),t:Set(val))->Set(val) Isempty(s:Set(val))->Bool Member-of(v:val,s:Set(val))->Bool Choose(s:Set(val))->val Remove(v:val,s:Set(val))->Set(val) Equal(s:Set(val),t:Set(val))->Bool Subset(s:Set(val),t:Set(val))->Bool Specifikation Alla metoder som finns i boken behövs inte. Empty, IsEmpty, Insert, Choose och Remove skulle räcka. Man har tagit med de vanliga matematiska mängdoperationerna. Vill man kunna ha mängder av mängder måste man skapa en ny datatyp generaliserad mängd på samma sätt som generaliserad lista. Konstruktion av mängd Nästan oavsett konstruktion måste man kunna hantera det faktum att det inte får finnas dubbletter i en mängd. Mängd som lista har två alternativ: Se till att listan inte har dubbletter (krav på Set-Insert och Union) Låt listan ha dubbletter (krav på Equal, Remove, Intersection, Difference) 6 7
Konstruktion av mängd som lista Komplexitet: Metoder som kräver sökningar i listan O(n) Binära mängdoperationerna kräver (pga nästlad iteration) O(m*n) Listan kan konstrueras på olika sätt. Sorterad lista tex är effektivare för de binära mängdoperationerna. + Grundmängden kan vara mycket stor + Delmängderna kan både vara mycket små och mycket stora utan utrymmesförluster (om man implementerar listan på rätt sätt) - Flera operationer blir slöa jämfört med andra val. Konstruktion av mängd som bitvektor En bitvektor är en vektor med elementvärden av typen Bit {,} Ibland tolkas och som falskt resp. sant, dvs Bit identifieras som datatypen Boolean Grundmängden måste ha en diskret linjär ordning av elementen. (Man kan numrera dem.) Bit k i bitvektorn motsvarar det k:te elementet i grundmängden. Biten är om elementet ingår i mängden. 9 Konstruktion av mängd som bitvektor Om bitvektorn finns implementerad som ett eller flera ord i datorns primärminne kan man utnyttja maskinoperationer. Dvs man gör operationen samtidigt på alla element i vektorn. Detta gör många metoder effektiva. + Relativt effektiv i allmänhet, mycket effektiv som ovan. - Grundmängden måste vara ändlig och i praktiken rätt så liten. Reserverar minne proportionellt mot grundmängdens storlek. - Om man har många små mängder i en tillämpning blir det dålig effektivitet i minnesutnyttjandet. Mängd konstruerad som boolesk funktion En mängd kan representeras av sin karakteristiska funktion χ s (x): χ s (x) om x S annars Lexikon En förenklad mängd där man tagit bort union, intersection, difference, subset och equal. Kvar är det man behöver för att kunna hålla ordning på en samling objekt: empty isempty(s) insert(v, s) remove(v, s) memberof(v, s) Lexikon Ett lexikon är som en tabell utan tabellvärden. Saknar alltså metoder för att kombinera lexikon till nya lexikon. Lexikon är en solitär datatyp. Man kan bara göra modifieringar av isolerade dataobjekt. En icke-solitär datatyp har stöd för att kombinera/bearbeta två eller flera dataobjekt.
Konstruktion av lexikon Vi kan få betydligt effektivare konstruktioner när antalet och typen av metoder är begränsade. Vi kommer titta på två konstruktioner av lexikon: Lexikon som Hashtabell Lexikon som Binärt sökträd (nästa föreläsning) Eftersom Lexikon är så likt Tabell kan dessa konstruktioner med små ändringar bli effektiva konstruktioner av en tabell. Hashtabell Problemen med att implementera en mängd/lexikon som en bitvektor är att grundmängden inte får bli för stor och att många små mängder slösar med minnet. Vore bra att kunna komprimera bitvektorerna. Vi behöver en hashfunktion som avbildar den större indextypen A till en mindre indextyp B. Man kan också se det som att grundmängden är nycklarna i en tabell. Eller att man vill omvandla nycklar i en tabell till siffror med hjälp av hashfunktionen. Hashfunktion Funktionen f(x) avbildar en stor mängd nycklar, A, på en liten mängd tal, B. Kollisioner är oundvikliga och en bra hashfunktion kännetecknas av: Litet förväntat antal kollisioner Sprider elementen jämt över mängden. Bör påverkas av alla delar av nyckeln Kollisioner kan hanteras med Sluten hashing Öppen hashing Sluten hashing Låt B vara en cirkulär vektor, dvs efter det sista elementet följer det första. Linjär teknik för kollisioner Om ett element kolliderar (platsen redan upptagen) sätter man in det på den första lediga platsen efter den upptagna. Ger upphov till klustring i tabellen. Sökning efter ett element börjar på den plats dess hashvärde anger och fortsätter eventuellt framåt. Om det inte påträffats före nästa lediga plats så finns det inte i tabellen. Om vi vid borttagning i tabellen bara lämnar platsen tom blir det fel vid sökning. Sätt in en borttagen markör i tabellen. 6 7 Exempel: Sluten hashing, linjär teknik Grundmängden,,, ska avbildas på värdena,,..., 6. Mängd {,, 7, 6, } Använd hashfunktionen f(x) x % 7. h() h() 6 6 6 7 6 7 6 7 h(7) 6 6 h(6) 6 h() 6 Sluten hashing, linjär teknik Värstafallskomplexiteten för samtliga operationer är O(n) där n är antalet element som finns insatta i tabellen. Är dock ytterst osannolikt. Alla element måste ligga i en följd. Under förutsättning att tabellen inte fylls mer än till en viss del får man i medeltal O() för operationerna. 9
Hashtabeller - fyllnadsgrad En hashtabells fyllnadsgrad (λ) definieras som kvoten mellan antalet insatta element och antalet platser i tabellen. En tom tabell har λ och en full λ. Man vet att Medelantalet platser som måste prövas vid en insättning och misslyckad sökning är uppåt begränsad av (+/(- λ) ) /, (λ. ger.) Medelantalet platser för en lyckad sökning är uppåt begränsad av (+/(- λ))/, (λ. ger.) Sluten hashing, kvadratisk teknik När fyllnadsgraden blir hög (>7%) blir operationerna för den linjära tekniken långsam. Kvadratisk teknik kan användas istället: Vid kollision tas först nästa element, sedan det fyra platser fram sedan nio fram etc, dvs H, H+, H+,, H+i, där H är elementets hashvärde. Exempel: Sluten hashing, kvadratisk teknik Sätt in talen 9,, 9,, 9 i en tabell med platser. h(x) x % 6 7 9 9 9 9 9 % 9 % 9 % 9, upptagen prova H+ %, upptagen, H+9 upptagen, H+ 9 % 9, nästa lediga plats H+ Sluten hashing Kvadratisk teknik Inte säkert att man hittar en ledig plats även om det finns! Exempel: Man har en tabell som är 6 element stor och man använder hashfunktionen h(x) x % 6 Stoppar in elementen, 6, och 6. Då finns det inte plats för några fler tal som hashas till. De enda platser som kommer prövas är de upptagna,,, 9. Men: Om kvadratisk teknik används och tabellens storlek är ett primtal så kan ett nytt element alltid stoppas in om fyllnadsgraden <.. Fullständig komplexitetsanalys saknas men i praktiken ger den upphov till mindre klustring än linjär hashing. Öppen hashing Istället för cirkulär vektor används en vektor av lista. I lista nummer i ligger alla element med hashvärde i. Värstafallskomplexitet: O(n) för alla operationer (Alla element i samma lista). Medelfallskomplexitet: Insättning och misslyckad sökning blir n/tablesize Lyckad sökning blir (n-)/(*tablesize)+ Tumregel: Inte fler än *tablesize element bör sättas in. Exempel: Öppen hashing Grundmängden,,, ska avbildas på värdena,,..., 6. Mängd {, 7, 7, 6, } Använd hashfunktionen f(x) x % 7. 7 6 6 7
Från: http://www.ida.liu.se/labs/logpro/ulfni/dalg/resources/f.pdf Linear prob Sluten hashing med linjär teknik Chaining Öppen hashing Double hash Sluten hashing med kvadratisk teknik Hashfunktionen Vi har använt h(x) x % m i exemplen. Fungerar bra om m är ett primtal. Annars kan periodicitet uppstå. Om x tal så finns mer generella metoder, tex h(x) ((c *x + c ) % p) % m p stort primtal > m, tex c och c konstant heltal > och < p c ofta satt till 6 7