Föreläsning 7 Innehåll Mängd Specifikation Specifikation

Innehåll
• Mängd
• Lexikon
• Hashtabell
Föreläsning 7
Mängd, lexikon och
hashtabell
175
Mängd
176
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.
177
Specifikation
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
178
Konstruktion av mängd
• 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.
179
• 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)
180
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 = {0,1}
– Ibland tolkas 0 och 1 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 1 om
elementet ingår i mängden.
181
182
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) = 1 om x "S
!
0 annars
183
Lexikon
184
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)
185
• 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.
186
Konstruktion av lexikon
Hashtabell
• 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.
• 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.
187
188
Sluten hashing
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:
• 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.
– Litet förväntat antal kollisioner
• Ger upphov till klustring i tabellen.
– Sprider elementen jämt över mängden.
– 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.
– Bör påverkas av alla delar av nyckeln
• Kollisioner kan hanteras med
– Sluten hashing
– Öppen hashing
• Sätt in en ”borttagen” markör i tabellen.
189
Exempel: Sluten hashing, linjär teknik
• Grundmängden 1, 2, …, 125 ska avbildas på värdena
0, 1, ..., 6.
• Mängd = {1, 8, 27, 64, 125}
Använd hashfunktionen f(x) = x % 7.
0
1
2
3
4
0
1
h(1)
=
1
1
2
8
3
4
0
125
1
1
1
1
1
1
2
8
h(27) 2
= 3
6
4
8
h(64) 2
= 3
1
4
64
0
1
h(8)
=
1
0
3
4
5
5
5
6
6
6
64
6
h(125)
=
6
27
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
(1) för operationerna.
5
5
27
8
190
27
191
192
Hashtabeller - fyllnadsgrad
• En hashtabells fyllnadsgrad (#) definieras som
kvoten mellan antalet insatta element och
antalet platser i tabellen.
– En tom tabell har # = 0 och en full # = 1.
• Man vet att
Sluten hashing, linjär teknik
• När fyllnadsgraden blir hög (>75%) blir
operationerna för den linjära tekniken
långsam.
– Medelantalet platser som måste prövas vid en
insättning och misslyckad sökning är uppåt
begränsad av (1+1/(1- #)2 ) /2, (# = 0.5 ger 2.5)
– Medelantalet platser för en lyckad sökning är uppåt
begränsad av (1+1/(1- #))/2,
(# = 0.5 ger 1.5)
193
Sluten hashing, kvadratisk teknik
194
Exempel: Sluten hashing,
kvadratisk teknik
• Sätt in talen 89, 18, 49, 58, 9 i en tabell med 10
platser. h(x) = x % 10
• 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
+1, H+4, …, H+i2, … där H är elementets
hashvärde.
0
49
1
2
3
4
58 9
5
6
7
8
9
18 89
89 % 10 = 9
18 % 10 = 8
49 % 10 = 9, upptagen prova H+1 = 0
58 % 10 = 8, upptagen, H+1=9 upptagen, H+4=2
9 % 10 = 9, nästa lediga plats H+4=3
195
Sluten hashing – Kvadratisk teknik
• Inte säkert att man hittar en ledig plats även om det
finns! Exempel:
– Man har en tabell som är 16 element stor och man använder
hashfunktionen h(x) = x % 16
– Stoppar in elementen 0, 16, 32 och 64. Då finns det inte plats
för några fler tal som hashas till 0. De enda platser som
kommer prövas är de upptagna 0, 1, 4 , 9.
• Men:
– Om kvadratisk teknik används och tabellens storlek är ett
primtal så kan ett nytt element alltid stoppas in om
fyllnadsgraden < 0.5.
– Fullständig komplexitetsanalys saknas men i praktiken ger
den upphov till mindre klustring än linjär hashing.
197
196
Ö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-1)/(2*tableSize)+1
• Tumregel: Inte fler än 2*tableSize element bör
sättas in.
198
Från: http://www.ida.liu.se/labs/logpro/ulfni/dalg/resources/f8.pdf
Exempel: Öppen hashing
• Grundmängden 1, 2, …, 125 ska avbildas på
värdena 0, 1, ..., 6.
• Mängd = {1, 7, 27, 64, 125}
Använd hashfunktionen f(x) = x % 7.
0
1
2
Linear prob = Sluten hashing med
linjär teknik
Chaining = Öppen hashing
Double hash = Hashning med en
teknik som liknar den slutna med
kvadratisk teknik
7
1
64
27
125
3
4
5
6
199
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) = ((c1*x + c2) % p) % m
• p stort primtal > m, tex 1048583
• c1 och c2 konstant heltal > 0 och < p
• c2 ofta satt till 0
201
200