Hashing
description
Transcript of Hashing
1
Hashing
2
• HashingHashfunktionerKollisionsstrategierEffektivitetHashing i Javas biblioteker
• PrioritetskøerBinær hobAnvendelser: heapsort, ekstern sortering
Plan
3
Hashingsøgning ved nøgletransformation
Med balancerede træer foretages O(log2N) sammenligninger af nøgler.
Men er O(log2N) den bedst opnåelige kompleksitet?
Nej.
Hvordan opnås lavere kompleksitet?
Med hashing, en teknik, der benytter transformationer af nøgler til direkte at kunne referere til poster i en tabel.
Med hashing opnås under gunstige omstændigheder kompleksitet O(1).
4
Grundlæggende ide
Ideelt set burde to forskellige nøgler afbildes på to forskellige indices. At to eller flere nøgler afbildes på samme indeks kaldes en kollision.
En kollisionsstrategi er en algoritme til håndtering af kollisioner.
Gem hver post i en tabel på en plads, der er bestemt af postens nøgle.
En hashfunktion er en metode til beregning af et tabelindeks ud fra en nøgle.
Matematisk udtrykt: En hashfunktion er en afbildning af en mængde af nøgler på et indeksinterval.
h: K I
5
Tid/plads-afvejning(trade off)
Ingen pladsbegrænsninger: benyt nøglen som indeks (triviel hashfunktion)
Ingen tidsbegrænsninger: benyt sekventiel søgning
Hvis der er begrænsninger på både plads og tid: benyt hashing
6
Hashingteknikken
Lad h betegne hashfunktionen.
Indsættelse: En post med nøgle K placeres på indeks h(K), med mindre der i forvejen er en post på dette indeks. Så må posten placeres på anden måde (hvordan - afhænger af kollisionsstrategien).
Søgning: Ved søgning efter en post med nøgle K, undersøges først posten på indeks h(K). Hvis denne indeholder K, afsluttes søgningen med succes. Ellers fortsætter søgningen (hvordan - afhænger af kollisionsstrategien).
7
“Gode” hashfunktioner
• Kollisioner bør så vidt muligt undgås. Hashfunktionen bør sprede funktionsværdierne jævnt på hele indeksintervallet.
• Hashfunktionen bør være beregningsmæssigt billig.
8
Konstruktion af hashfunktioner
(Korte nøgler)
Korte nøgler (nøgler, der kan være i et maskinord):
Betragt nøglen som et heltal og beregn
h(K) = K mod M (i Java: K % M)
hvor M er tabelstørrelsen.
h(K) [0;M-1]
9
Nøgler bestående af 4 ascii-tegn, tabelstørrelse 101.
ascii a b c d hex 6 1 6 2 6 3 6 4 bin 01100001011000100110001101100100
Eksempel med korte nøgler
0x61626364 = 163383172416338831724 % 101 = 11Nøglen "abcd" hasher til 11.
0x64636261 = 16842348491684234849 % 101 = 57Nøglen "dcba" hasher til 57.
0x61626263 = 16338376671633837667 % 101 = 57Nøglen "abbc" hasher også til 57. Kollision!
10
Konstruktion af hashfunktioner
(Lange nøgler)
Lange nøgler (nøgler, der ikke kan være i et maskinord):
Betragt nøglen som et langt heltal og beregn
h(K) = K mod M
hvor M er tabelstørrelsen.
Altså i princippet som for korte nøgler.
11
Eksempel med lange nøgler
Eksempel med 4 tegn. Men metoden virker også for vilkårligt lange nøgler.
Benyt Horners regel:
0x61626364 =
97*2563 + 98*2562 + 99*2561 + 100 =
((97*256 + 98)*256 + 99)*256 + 100
Tag modulo efter hver addition for at undgå aritmetisk overløb:
(97*256 + 98 = 24930) % 101 = 84
(84*256 + 99 = 21603) % 101 = 90
(90*256 + 100 = 23140) % 101 = 11
12
Tabelstørrelsen
Vælg tabelstørrelsen som et primtal.Hvorfor?
I eksemplet før havde vi"abcd" = 0x61626364 =
97*2563 + 98*2562 + 99*2561 + 100
Hvis tabelstørrelsen vælges til 256, vil kun det sidste tegn have betydning ved beregning af h.
En simpel måde at sikre sig, at alle tegn bidrager, er at vælge tabelstørrelsen som et primtal.
13
int hash(String key, int tableSize) {
int h = 0;
for (int i = 0; i < key.length(); i++)
h = (h*37 + key.charAt(i)) % tableSize;
return h;
}
Eksempel på hashfunktion
For at sprede værdierne bedre er 256 erstattet med 37.
Modulo-beregningerne undervejs kan undværes:
int hash(String key, int tableSize) {
int h = 0;
for (int i = 0; i < key.length(); i++)
h = h*37 + key.charAt(i);
h %= tableSize;
return h < 0 ? h + tableSize : h;
}
14
public int hashCode() { int h = 0; int off = offset; char val[] = value; int len = count;
if (len < 16) { for (int i = len; i > 0; i--) h = (h * 37) + val[off++]; } else { // only sample some characters int skip = len / 8; for (int i = len; i > 0; i -= skip, off += skip) h = (h * 39) + val[off]; } return h;}
Javas implementering af hashCode i String(Java 1.1)
15
Javas implementering af hashCode i String(Java 1.2)
public int hashCode() {
int h = 0;
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++)
h = 31*h + val[off++];
return h;
}
16
Hyppigheden af kollisioner
Fødselsdagsparadokset: Hvor mange personer skal være forsamlet i et selskab, for at der er mere en 50% sandsynlighed for at mindst to personer har fødselsdag på samme dato?
Svar: 24.
Hvis M være tabelstørrelsen, hvor mange indsættelser kan da i gennemsnit foretages, før der opstår en kollision?
M 100 12 365 24 1000 40 10000 125
100000 396 1000000 2353
πM/2
17
Kollisionstrategier
Antal poster: NTabelstørrelse: M
Mulighed 1 (separat kædning): Tillad N > M:
Læg nøgler, der hasher til samme indeks, ind i en liste (med cirka N/M nøgler per liste).
Mulighed 2 (åben adressering):Sørg for at N < M: Læg kolliderende nøgler i tabellen.
18
Separat kædning
Simpel, praktisk og meget udbredt metode.Metode: M hægtede lister - en for hver tabelindgang.
0: * 1: L A W * 2: M X * 3: N C * 4: * 5: E P * (M = 11) 6: * (N = 14) 7: G R * 8: H S * 9: I *
10: *
Nedbringer den gennemsnitlige søgetid med en faktor M i forhold til sekventiel søgning.
19
Implementering af separat kædning
public interface HashTable { void put(Object key, Object value); Object get(Object key); void remove(Object key);}
public class SeparateChainingHashTable implements HashTable { private HashEntry[] array; ... }
20
class HashEntry { HashEntry(Object k, Object v, HashEntry n) { key = k; value = v; next = n; }
Object key, value; HashEntry next;}
21
void put(Object key, Object value) { int pos = Math.abs(key.hashCode()) % array.length; for (HashEntry e = array[pos]; e != null; e = e.next) if (key.equals(e.key)) return; array[pos] = new HashEntry(key, value, array[pos]); }
Object get(Object key) { int pos = Math.abs(key.hashCode()) % array.length; for (HashEntry e = array[pos]; e != null; e = e.next) if (key.equals(e.key)) return e.value; return null;}
22
void remove(Object key) { int pos = Math.abs(key.hashCode()) % array.length; for (HashEntry e = array[pos], prev = null; e != null; prev = e, e = e.next) if (key.equals(e.key)) { if (prev != null) prev.next = e.next; else array[pos] = e.next; return; }}
prev e e.next
23
Effektivitet af separat kædning
Indsættelse: N/M (i gennemsnit)
Mislykket søgning: N/M (i gennemsnit)
Succesfuld søgning: 1 + N/M/2 (i gennemsnit)
Værste tilfælde: N
Hvis listerne holdes sorteret:Tid for mislykket søgning mindskes til N/M/2
Tid for indsættelse øges til N/M/2
24
Åben adresseringLineær prøvning
Åben adressering:Ingen hægter. Alle poster opbevares i tabellen.
Lineær prøvning: Start lineær søgning fra hashpositionen, og stands ved den søgte post eller en tom position.
Stadig konstant søgetid, hvis M er tilstrækkelig stor.
25
Et simpelt eksempel
Mængden af nøgler er alfabetets store bogstaver. Der er ingen information tilknyttet nøglerne. Tabelstørrelsen er 7.
h(K) = (K’s nummer i alfabetet) mod 7 = (K - ‘A’ + 1) % 7
0123456
26
h(N) = 14 % 7 = 0
h(C) = 3 % 7 = 3
h(K) = 11 % 7 = 4
h(S) = 19 % 7 = 5
0123456
C
S
N
K
Tabel efter indsættelse af nøglerne
C, K, N, S
27
Indsættelse af Y giver kollision
h(Y) = 25 % 7 = 4
0123456
C
S
N
K
Placering efter 3 forsøg
0123456
C
S
N
K
Y
28
Indsættelse af D
h(D) = 4 % 7 = 4
0123456
C
S
N
K
Y
Placering efter 5 forsøg(med “wrap around”)
0123456
C
S
N
K
Y
D
Bemærk: tabellen må ikke blive fuld!
29
Implementering af åben adressering
abstract class ProbingHashTable implements HashTable { ProbingHashTable() { array = new HashEntry[size]; } void put(Object key, Object value) { ... } Object get(Object key) { ... } void remove(Object key) { ... }
abstract protected int findPos(Object key); private HashEntry[] array; private int currentSize; }
30
Object get(Object key) { int pos = findPos(key); if (array[pos] == null || !array[pos].isActive) return null; return array[pos].value; }
class HashEntry { HashEntry(Object k, Object v) { key = k; value = v; } Object key, value; boolean isActive = true;}
void remove(Object key) { int pos = findPos(key); if (array[pos] == null) return; array[pos].isActive = false;}
31
void put(Object key, Object value) { int pos = findPos(key); array[pos] = new HashEntry(key, value); if (++currentSize < array.length / 2) return; // rehash HashEntry[] oldArray = array; array = new HashEntry[nextPrime(2 * oldArray.length)]; currentSize = 0; for (int i = 0; i < oldArray.length; i++) if (oldArray[i] != null && oldArray[i].IsActive) put(oldArray[i].key, oldArray[i].value);}
Tidsforbruget for nextPrime er O(√N * logN).
Ved rehash er simpel kopiering utilstrækkelig.
32
class LinearProbingHashTable extends ProbingHashTable { protected int findPos(Hashable key) { int pos = Math.abs(key.hashCode()) % array.length; while (array[pos] != null && !array[pos].key.equals(key)) pos = (pos + 1) % array.length; return pos; } }
Klassen LinearProbingHashTable
33
Effektivitet af lineær prøvning
Tyndt besat tabel: ligesom separat kædning.
Lineær prøvning bruger gennemsnitligt færre end 5 forsøg for en hashtabel, der er mindre end 2/3 fuld.
De præcise udtryk er:
forsøg ved mislykket søgning, og
forsøg ved succesfuld søgning,
hvor = N/M betegner fyldningsgraden.
12+ 1
2(1−α)2
12+ 1
2(1−α)
34
Effektivitetskurver for lineær prøvning
Mislykket søgning Succesfuld søgning
αα
12+
1
2(1−α)
12+
1
2(1−α)2
35
Argumentation for tendens til klyngedannelse
Så vil chancen for, at en ny post placeres på position j+1 være lig med chancen for, at en ny post skal placeres i intervallet [i:j+1].
For at den nye post placeres på j+2, skal dens hashværdi derimod være præcis j+2.
i-1 j+1 j+2
Antag at alle positioner [i:j] indeholder poster, mens i-1, j+1 og j+2 er tomme.
36
Klyngedannelse
Uheldigt fænomen.
Lange klynger har en tendens til at blive længere.
Søgelængen vokser drastisk, efterhånden som tabellen fyldes.
Lineær prøvning er for langsom, når tabellen bliver 70-80% fuld.
37
Kvadratisk prøvning(reducerer risikoen for klyngedannelse)
Prøvningssekvens: Lineær prøvning: pos, pos + 1, pos + 2, pos + 3, ... Kvadratisk prøvning: pos, pos + 12, pos + 22, pos + 32, ...
Lad Hi betegne den i’te position (H0 er startpositionen).
Idet Hi-1 = pos + (i - 1)2 = pos + i2 - 2i + 1 = Hi - 2i + 1
fåsHi = Hi-1 + 2i - 1
Kvadrering kan undgås:
38
Implementering af kvadratisk prøvning
class QuadraticProbingTable extends ProbingHashTable { protected int findPos(Object key) { int pos = key.hashCode() % array.length; int i = 0; while (array[pos] != null && !array[pos].element.equals(key)) pos = (pos + 2 * ++i - 1) % array.length; return pos; } }
Det kan bevises, at
Hvis fyldningsgraden er mindre end 0.5, er indsættelse altid mulig, og under indsættelsen vil ingen indgang blive prøvet mere end én gang.
39
Dobbelt hashing
Undgå klyngedannelse ved at bruge en ekstra hashfunktion.
I stedet for som i lineær prøvning at prøve successive indgange, foretages prøvningen med en fast afstand bestemt af den anden hashfunktion.
Derved øges sandsynligheden for at finde tomme indgange ved indsættelse.
40
Implementering af dobbelt hashing
class DoubleHashTable extends ProbingHashTable { protected int findPos(Object key) { int pos = Math.abs(key.hashCode()) % array.length; while (array[pos] != null && !array[pos].element.equals(key)) pos = (pos + key.hash2()) % array.length; return pos; } }
41
Krav til den andenhashfunktion
• Den bør ikke returne 0.
• Den skal altid returnere værdier, der er primiske med M.Kan opnås ved at vælge M som et primtal og lade h2(k) <
M for ethvert k.
• Den skal være forskellig fra den første.
En simpel og hurtig metode er
h2(k) = 8 - k % 8 (k % 8 er de sidste 3 bit af k)
42
Effektivitet af dobbelt hashing
Dobbelt hashing bruger gennemsnitligt færre forsøg end lineær prøvning. Færre end 5 forsøg ved en søgning, når tabellen højst er 80% fuld, og færre end 5 forsøg ved en succesfuld søgning, når tabellen højst er 99% fuld.
De præcise udtryk er:
forsøg ved mislykket søgning, og
forsøg ved succesfuld søgning,
hvor er fyldningsgraden.
11−α
−ln(1−α )α
43
Dobbelt hashing contra lineær prøvning
αα
−ln(1−α)
α
11−α
Mislykket søgning Succesfuld søgning
dobbelt hashing
Mislykket søgning Succesfuld søgning
αα
lineær prøvning
44
Fordele ved separat kædning
• Idiotsikker metode (bryder ikke sammen)
• Antallet af poster behøver ikke at være kendt på forhånd
• Sletning er simpel
• Tillader ens nøgler
45
Hashing i Javaclass HashMap
public class HashMap {public
HashMap(int initialCapacity, float loadFactor); public HashMap(int initialCapacity);
public HashMap() { this(101, 0.75); }
public Object put(Object key, Object value);public Object get(Object key);
public Object remove(Object key);
public int size(); public
boolean isEmpty();public boolean containsKey(Object key);public boolean contains(Object value); public void clear();
public Set keySet(); public
Collection values();public Set EntrySet();
}
46
Metoden get(benytter separat kædning)
class Entry { Object key; Object value; Entry next; int hash;}
private Entry[] table; private int count;private int threshold;private float loadFactor;
public Object get(Object key) { int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; for (Entry e = table[index]; e != null; e = e.next) if (e.hash == hash && e.key.equals(key)) return e.value; return null; }
47
Metoden put
public Object put(Object key, Object value) { int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; for (Entry e = tab[index]; e != null; e = e.next) if (e.hash == hash && e.key.equals(key)) { Object old = e.value; e.value = value; return old; } if (count >= threshold) { rehash(); return put(key, value); } Entry e = new Entry(); e.hash = hash; e.key = key; e.value = value; e.next = table[index]; table[index] = e; count++; return null; }
48
Metoden rehash
protected void rehash() { int oldCapacity = table.length; Entry[] oldTable = table; int newCapacity = oldCapacity * 2 + 1; Entry newTable[] = new Entry[newCapacity]; threshold = (int) (newCapacity * loadFactor); table = newTable; for (int i = oldCapacity; i-- > 0; ) { for (Entry old = oldTable[i]; old != null; ) { HashtableEntry e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = newTable[index]; newTable[index] = e;
} }}
49
Metoden remove
public Object remove(Object key) { int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && e.key.equals(key)) { if (prev != null) prev.next = e.next; else table[index] = e.next; count--; return e.value;
} } return null;}
50
Grunde til ikke at bruge hashing
Hvorfor bruge andre metoder?
• Der er ingen effektivitetsgaranti
• Hvis nøglerne er lange, kan hashfunktionen være for kostbar at beregne
• Bruger ekstra plads
• Understøtter ikke sortering
51
Prioritetskøer
52
Prioritetskøer
En prioritetskø er en abstrakt datatype, der repræsenterer en endelig mængde af dataelementer forsynet med numeriske nøgler (prioriteter). Til typen er knyttet følgende 2 operationer:
insert(x): tilføj dataelementet x til mængden
deleteMin: find og fjern dataelementet med den laveste prioritet
En stak og en kø er specialtilfælde af en prioritetskø.
53
Anvendelser af prioritetskøer
• operativsystemer
• grafsøgning
• datakomprimering
• diskret simulering
• sortering
54
Specifikation i Java
Undertiden er det hensigtsmæssigt med yderligere operationer, f.eks.:
boolean isEmpty(): afgør om prioritetskøen er tom void join(PriorityQueue pq): forener prioritetskøen med en anden prioritetskø (pq).
public interface PriorityQueue { void insert(Comparable x); Comparable deleteMin(); }
55
Implementering ved hjælp af et uordnet arrayclass ArrayPriorityQueue implements PriorityQueue { private Comparable[] array; private int currentSize;
ArrayPriorityQueue() { array = new Comparable[DEFAULT_CAPACITY]; } void insert(Comparable x) { checkSize(); array[currentSize++] = x; }
Comparable deleteMin() { if (currentSize == 0) throw new UnderflowException();
int min = 0; for (int i = 1; i < currentSize; i++)
if (array[i].compareTo(array[min]) < 0) min = i; swapReferences(array, min, currentSize - 1); return array[--currentSize]; }}
56
Implementering ved hjælp af et ordnet array
Arrayet holdes sorteret i faldende orden
Andre elementære implementationer: uordnede lister, ordnede lister
void insert(Comparable x) { checkSize(); int i = currentSize; while (i > 0 && array[i - 1].compareTo(x) < 0)) { a[i] = a[i - 1]; i--; } array[i] = x; currentSize++;}
Comparable deleteMin() { if (currentSize == 0) throw new UnderflowException(); return array[currentSize--]; }
57
Sortering af arrayet a i stigende orden:
Sortering ved hjælp afprioritetskø
Hvis prioritetskøen er implementeret som et uordnet array, svarer algoritmen til sortering ved udvælgelse.
Hvis prioritetskøen er implementeret som et ordnet array, svarer algoritmen til sortering ved indsættelse.
PriorityQueue pq = new TypePriorityQueue();for (int i = 0; i < a.length; i++) pq.insert(a[i]);for (int i = 0; i < a.length; i++) a[i] = pq.deleteMin();
58
Implementering ved hjælp af et binært søgetræ
void insert(Comparable x) { searchTree.insert(x);}
Comparable deleteMin() { return searchTree.removeMin();}
Hvis søgetræet holdes balanceret, er køretiden for begge operationer O(logN).
Imidlertid er implementering vanskelig (specielt removeMin).
59
Binær hob
Komplet træ:
Heraf kan udledes: Den mindste nøgle findes i roden.
(1) alle niveauer, eventuelt med undtagelse af det nederste,er fyldt ud med knuder;
(2) knuderne på det nederste niveau er placeret helt til venstre
En binær hob er et komplet binært træ (strukturbetingelse), hvor nøglen i enhver knude er mindre end eller lig med sine børns nøgler (ordningsbetingelse).
60
Eksempel på hob
En hob kan repræsenteres i et array (ikke behov for hægter):roden: array[1]sønner af roden: array[2] og array[3]sønner af i: array[2*i] og array[2*i+1]far til i: array[i/2]
i: 0 1 2 3 4 5 6 7 8 9 10 array: 13 21 16 24 31 19 68 65 26 32
13
21
31 19 68
16
322665
24
(level order, implicit repræsentation)
1
2 3
4
8 10
5 6 7
9
61
Indsættelse af element i hob
Indsæt elementet som det sidste.
13
21
31 19 68
16
322665
24
14
Indsættelse af 14
13
14
21 19 68
16
322665
24
31
Oprethold ordningsbetingelsen ved fortsat at ombytte med faderen, så længe det er nødvendigt.
62
Implementering af insert i Java
void insert(Comparable x) { checkSize(); array[++currentSize] = x; percolateUp(currentSize);}
void percolateUp(int hole) { Comparable x = array[hole]; array[0] = x; for ( ; x.compareTo(array[hole] / 2) < 0; hole /= 2) array[hole] = array[hole / 2]; array[hole] = x; }
percolate: sive igennem
63
Fjernelse af roden i en hob
Erstat roden med det sidste element.
13
14
21 19 68
16
322665
24
31
Sletning af 13
14
21
31 19 68
16
322665
24
Opret ordningsbetingelsen ved fortsat at ombytte med den mindste af sønnerne, så længe det er nødvendigt.
64
Implementering af remove i Java
Comparable deleteMin() { if (isEmpty()) throw new UnderflowException(); Comparable minItem = array[1]; array[1] = array[currentSize--]; percolateDown(1); return minItem;}
65
void percolateDown(int hole) { int child; Comparable tmp = array[hole]; for ( ; hole * 2 <= currentSize; hole = child) { child = hole * 2; if (child != currentSize && array[child + 1].compareTo(array[child]) < 0) child++; if (array[child].compareTo(tmp) < 0) array[hole] = array[child]; else break; } array[hole] = tmp;}
child ?
hole
66
Konstruktion af hob
Problem: Givet et array array[1:N] elementer i vilkårlig orden. Omordn arrayet, således at det udgør en hob.
Top-til-bund-konstruktion: Induktionshypotese: array[1:i] er en hob.
67
Kompleksitet:
O( i=2..N log(i)) = O(N log N)
for (int i = 2; i <= N; i++) percolateUp(i);
68
Bund-til-top-konstruktion: Induktionshypotese: Alle træer repræsenteret i array[i+1:N] tilfredsstiller hob-betingelserne.
for (int i = N / 2; i >= 1; i--)
percolateDown(i);
Kompleksitet: O( i=1..N/2 log(N/i)) = O(N)
array[N/2+1:N] tilfredsstiller hob-betingelserne (de er blade i træet).
69
Heapsort(der benyttes en max-heap)
• Kompleksiteten af heapsort er O(N logN).• Ingen brug for ekstra plads.
void heapsort(Comparable[] a) { for (int i = a.length / 2; i >= 1; i--) percDown(a, i, a.length); for (int i = a.length - 1; i > 0; i--) { swapReferences(a, 0, i); percDown(a, 0, i); } }
70
Eksempel på sortering
59 26 58 21 41 97 21 16 26 53
Dan max-hob:
97 53 59 26 41 58 31 16 21 36
59 53 58 26 41 36 31 16 21 97
58 53 36 26 41 21 31 16 59 97
53 41 36 26 16 21 31 58 59 97
41 31 36 26 16 21 53 58 59 97
fortsættes
97
53
26 41 58
59
31
16 21 36
71
41 31 36 26 16 21 53 58 59 97
36 31 21 26 16 41 53 58 59 97
31 26 21 16 36 41 53 58 59 97
26 16 21 31 36 41 53 58 59 97
21 16 26 31 36 41 53 58 59 97
16 21 26 31 36 41 53 58 59 97
slut
41
31
26 16
21
72
Animering af heapsort(hob-konstruktions-fase)
73
Animering af heapsort(sorteringsfase)
74
Tid i sekunder:
Metode N = 32000 64000 128000 256000 512000 1024000
heapsort 0.03 0.07 0.16 0.32 0.94 2.10
mergesort 0.06 0.12 0.25 0.54 1.15 2.67
quicksort 0.03 0.05 0.09 0.17 0.36 0.76
shellsort 0.04 0.08 0.18 0.41 1.03 2.37
Empirisk undersøgelse af heapsort
75
Ekstern sortering(sortering på eksterne lagermedier)
Særlige hensyn:
(1) Det er tidsmæssigt dyrt at tilgå et dataelement
(2) Der er begrænsninger på tilgangen, f.eks. kan et magnetbånd kun læses
sekventielt.
76
Fordel-og-flet
Fordeling:Opdel filen i blokke på størrelse med det indre lager.Sorter hver af disse blokke og fordel dem på 2 eller flere bånd.
Fletning:Flet de sorterede blokke til længere sorterede blokke.Fortsæt på denne måde indtil hele filen er én sorteret blok.
77
Balanceret flervejsfletningEksempel: 3-vejsfletning af 81 poster
Sorterede blokke (længden målt i antal poster)
1 9 (3) 0 1 (27) 02 9 (3) 0 1 (27) 03 9 (3) 0 1 (27) 04 0 3 (9) 0 1 (81)5 0 3 (9) 0 6 0 3 (9) 0
3 passager for at sortere 81 poster
Fletningen kan foretages ved hjælp af en prioritetskø.
78
Balanceret flervejsfletning
N: antal poster
M: størrelse af indre lager (målt i antal poster)
Benyt halvdelen af båndene som indbånd, resten til udbånd.
Passage 0: fordel filen i sorterede blokke af størrelse Mpå bånd 1, 2, ..., k
Passage 1: k-flet blokkene fra bånd 1, 2, .., k til blokke af størrelse kM ud på bånd k+1, k+2, ..., 2k
Passage 2: k-flet blokkene fra bånd k+1, k+2, ..., 2k tilblokke af størrelse k2M ud på bånd 1, 2, ..., k.
...Passage p: k-flet blokkene fra indbåndene til en blok af
størrelse kpM ud på et af på udbåndene.
79
Filen er sorteret, når kpM ≥ N
d.v.s. efterp = logk(N/M) passager.
Eksempel:filstørrelse 109 posterlagerstørrelse 106 poster
antal båndstationer 4antal passager log2103 = 10
antal båndstationer 20antal passager log10103 = 3
Filen kan sorteres på 3-10 gange den tid, det vil tage at læse eller skrive den.
80
Polyfasesortering
Reducerer antallet af bånd til cirka det halve af antallet af bånd ved balanceret flervejsfletning.
Princip: Benyt hele tiden k-1 indbånd og 1 udbånd.
Algoritme:Foretag fletning fra de k-1 indbånd ud på udbåndet, indtil et af indbåndene bliver tomt.
Det tomme bånd tilbagespoles og benyttes som nyt udbånd for fletning fra de øvrige k-1 bånd.
Således fortsættes, indtil filen er sorteret.
81
Blokfordeling ved polyfasesortering
Fordeling: Fordel blokkene på k-1 bånd, således at den sidste fletning
gør alle indbånd tomme samtidigt.
Fordelingsmønsteret kan bestemmes ved brug af generaliserede Fibonaccital.
1 21 8 0 5 3 1 0 12 13 0 8 3 0 2 1 03 0 13 5 0 2 0 1 0
3 bånd, 34 blokke, 7 passager
Fk(N) = Fk (N-1) + Fk (N-2) + ... + Fk (N-k)Fk (0 ≤ N ≤ k-2) = 0, Fk (k-1) = 1
82
Afløserteknikken(engelsk: replacement selection)
En teknik, der muliggør at blokkene i fordelingsfasen kan blive af længde 2M.Organiser posterne i det inde lager som en hob.Når lageret er fyldt, og der ankommer en ny post, skrives den mindste post fra hoben ud på båndet og afløses af den nye post.
Der er nu 2 tilfælde:(1) Den nye post er større end eller lig med den sidst udskrevne post.
Posten indsættes i hoben.(2) Den nye post er mindre end den sidst udskrevne post.
Posten tilbageholdes indtil videre.
En blok er udskrevet, når alle poster i det indre lager er tilbageholdt (hoben er tom). Derefter organiseres de tilbageholdte poster som en hob, og der fortsættes som hidtil.
83
• Løs følgende opgaver
Opgave 46: 20.5 (1 point)
Opgave 47: 21.3 (2 point)
Opgave 48: 21.7 (1 point)
Opgave 49: 21.22 (1 point, ikke-obligatorisk)
Opgave 50: 21.23 (1 point, ikke-obligatorisk)
Afleveringsfrist: tirsdag den 11. december
Ugeseddel 124. december - 11. december