Præsentation er lastning. Vent venligst

Præsentation er lastning. Vent venligst

1 Hashing. 2 Hashing Hashfunktioner Kollisionsstrategier Effektivitet Hashing i Javas biblioteker Prioritetskøer Binær hob Anvendelser: heapsort, ekstern.

Lignende præsentationer


Præsentationer af emnet: "1 Hashing. 2 Hashing Hashfunktioner Kollisionsstrategier Effektivitet Hashing i Javas biblioteker Prioritetskøer Binær hob Anvendelser: heapsort, ekstern."— Præsentationens transcript:

1 1 Hashing

2 2 Hashing Hashfunktioner Kollisionsstrategier Effektivitet Hashing i Javas biblioteker Prioritetskøer Binær hob Anvendelser: heapsort, ekstern sortering Plan

3 3 Hashing søgning ved nøgletransformation Med balancerede træer foretages O(log 2 N) sammenligninger af nøgler. Men er O(log 2 N) 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 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 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 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 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 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 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 bin01100001011000100110001101100100 Eksempel med korte nøgler 0x61626364 = 1633831724 16338831724 % 101 = 11 Nøglen "abcd" hasher til 11. 0x64636261 = 16842348491684234849 % 101 = 57 Nøglen "dcba" hasher til 57. 0x61626263 = 16338376671633837667 % 101 = 57 Nøglen "abbc" hasher også til 57. Kollision!

10 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 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*256 3 + 98*256 2 + 99*256 1 + 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 12 Tabelstørrelsen Vælg tabelstørrelsen som et primtal. Hvorfor? I eksemplet før havde vi " abcd " = 0x61626364 = 97*256 3 + 98*256 2 + 99*256 1 + 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 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 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 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 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

17 17 Kollisionstrategier Antal poster: N Tabelstø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 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 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 20 class HashEntry { HashEntry(Object k, Object v, HashEntry n) { key = k; value = v; next = n; } Object key, value; HashEntry next; }

21 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 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; } prevee.next

23 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 24 Åben adressering Lineæ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 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 0 1 2 3 4 5 6

26 26 h(N) = 14 % 7 = 0 h(C) = 3 % 7 = 3 h(K) = 11 % 7 = 4 h(S) = 19 % 7 = 5 0 1 2 3 4 5 6 C S N K Tabel efter indsættelse af nøglerne C, K, N, S

27 27 Indsættelse af Y giver kollision h( Y ) = 25 % 7 = 4 0 1 2 3 4 5 6 C S N K Placering efter 3 forsøg 0 1 2 3 4 5 6 C S N K Y

28 28 Indsættelse af D h( D ) = 4 % 7 = 4 0 1 2 3 4 5 6 C S N K Y Placering efter 5 forsøg (med “wrap around”) 0 1 2 3 4 5 6 C S N K Y D Bemærk: tabellen må ikke blive fuld!

29 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 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 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 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 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.

34 34 Effektivitetskurver for lineær prøvning Mislykket søgningSuccesfuld søgning  

35 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-1j+1j+2 Antag at alle positioner [i:j] indeholder poster, mens i-1, j+1 og j+2 er tomme.

36 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 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 + 1 2, pos + 2 2, pos + 3 2,... Lad H i betegne den i’te position (H 0 er startpositionen). Idet H i-1 = pos + (i - 1) 2 = pos + i 2 - 2i + 1 = H i - 2i + 1 fås H i = H i-1 + 2i - 1 Kvadrering kan undgås:

38 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 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 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 41 Krav til den anden hashfunktion 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 h 2 (k) < M for ethvert k. Den skal være forskellig fra den første. En simpel og hurtig metode er h 2 (k) = 8 - k % 8(k % 8 er de sidste 3 bit af k)

42 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.

43 43 Dobbelt hashing contra lineær prøvning   Mislykket søgningSuccesfuld søgning dobbelt hashing Mislykket søgning Succesfuld søgning   lineær prøvning

44 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 45 Hashing i Java class 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 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 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 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 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 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 51 Prioritetskøer

52 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 53 Anvendelser af prioritetskøer operativsystemer grafsøgning datakomprimering diskret simulering sortering

54 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 55 Implementering ved hjælp af et uordnet array class 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 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 57 Sortering af arrayet a i stigende orden: Sortering ved hjælp af prioritetskø 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 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 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 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 32 2665 24 (level order, implicit repræsentation) 1 2 3 4 810 567 9

61 61 Indsættelse af element i hob Indsæt elementet som det sidste. 13 21 31 19 68 16 32 2665 24 14 Indsættelse af 14 13 14 21 19 68 16 32 2665 24 31 Oprethold ordningsbetingelsen ved fortsat at ombytte med faderen, så længe det er nødvendigt.

62 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 63 Fjernelse af roden i en hob Erstat roden med det sidste element. 13 14 21 19 68 16 32 2665 24 31 Sletning af 13 14 21 31 19 68 16 32 2665 24 Opret ordningsbetingelsen ved fortsat at ombytte med den mindste af sønnerne, så længe det er nødvendigt.

64 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 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 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 67 Kompleksitet: O(  i=2..N  log(i)) = O(N log N) for (int i = 2; i <= N ; i++) percolateUp(i);

68 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 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 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 3659 53 58 26 41 36 31 16 21 9758 53 36 26 41 21 31 16 59 9753 41 36 26 16 21 31 58 59 9741 31 36 26 16 21 53 58 59 97 fortsættes 97 53 264158 59 31 162136

71 71 41 31 36 26 16 21 53 58 59 9736 31 21 26 16 41 53 58 59 9731 26 21 16 36 41 53 58 59 9726 16 21 31 36 41 53 58 59 9721 16 26 31 36 41 53 58 59 9716 21 26 31 36 41 53 58 59 97 slut 41 31 2616 21

72 72 Animering af heapsort (hob-konstruktions-fase)

73 73 Animering af heapsort (sorteringsfase)

74 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 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 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 77 Balanceret flervejsfletning Eksempel: 3-vejsfletning af 81 poster Sorterede blokke (længden målt i antal poster) 1 9 (3)01 (27) 0 2 9 (3)01 (27) 0 3 9 (3)0 1 (27) 0 4 0 3 (9)0 1 (81) 5 03 (9)0 6 03 (9)0 3 passager for at sortere 81 poster Fletningen kan foretages ved hjælp af en prioritetskø.

78 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 M på 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 til blokke af størrelse k 2 M ud på bånd 1, 2,..., k.... Passage p: k-flet blokkene fra indbåndene til en blok afstørrelse k p M ud på et af på udbåndene.

79 79 Filen er sorteret, når k p M ≥ N d.v.s. efter p = log k (N/M) passager. Eksempel: filstørrelse10 9 poster lagerstørrelse10 6 poster antal båndstationer 4 antal passager log 2 10 3 =10 antal båndstationer20 antal passager log 10 10 3 = 3 Filen kan sorteres på 3-10 gange den tid, det vil tage at læse eller skrive den.

80 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 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. 121 80 5310 1 2130 830210 3 0 13 502010 3 bånd, 34 blokke, 7 passager F k (N) = F k (N-1) + F k (N-2) +... + F k (N-k) F k (0 ≤ N ≤ k-2) = 0, F k (k-1) = 1

82 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 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 12 4. december - 11. december


Download ppt "1 Hashing. 2 Hashing Hashfunktioner Kollisionsstrategier Effektivitet Hashing i Javas biblioteker Prioritetskøer Binær hob Anvendelser: heapsort, ekstern."

Lignende præsentationer


Annoncer fra Google