Hashing

83
1 Hashing

description

Hashing. Plan. Hashing HashfunktionerKollisionsstrategierEffektivitetHashing i Javas biblioteker Prioritetskøer Binær hobAnvendelser: heapsort, ekstern sortering. Hashing søgning ved nøgletransformation. - PowerPoint PPT Presentation

Transcript of Hashing

Page 1: Hashing

1

Hashing

Page 2: Hashing

2

• HashingHashfunktionerKollisionsstrategierEffektivitetHashing i Javas biblioteker

• PrioritetskøerBinær hobAnvendelser: heapsort, ekstern sortering

Plan

Page 3: Hashing

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

Page 4: Hashing

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

Page 5: Hashing

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

Page 6: 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).

Page 7: Hashing

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.

Page 8: Hashing

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]

Page 9: Hashing

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!

Page 10: Hashing

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.

Page 11: Hashing

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

Page 12: Hashing

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.

Page 13: Hashing

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;

}

Page 14: Hashing

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)

Page 15: Hashing

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;

}

Page 16: Hashing

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

Page 17: Hashing

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.

Page 18: Hashing

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.

Page 19: Hashing

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; ... }

Page 20: Hashing

20

class HashEntry { HashEntry(Object k, Object v, HashEntry n) { key = k; value = v; next = n; }

Object key, value; HashEntry next;}

Page 21: Hashing

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;}

Page 22: Hashing

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

Page 23: Hashing

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

Page 24: Hashing

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.

Page 25: Hashing

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

Page 26: Hashing

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

Page 27: Hashing

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

Page 28: Hashing

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!

Page 29: Hashing

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; }

Page 30: Hashing

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;}

Page 31: Hashing

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.

Page 32: Hashing

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

Page 33: Hashing

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−α)

Page 34: Hashing

34

Effektivitetskurver for lineær prøvning

Mislykket søgning Succesfuld søgning

αα

12+

1

2(1−α)

12+

1

2(1−α)2

Page 35: Hashing

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.

Page 36: Hashing

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.

Page 37: Hashing

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:

Page 38: Hashing

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.

Page 39: Hashing

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.

Page 40: Hashing

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; } }

Page 41: Hashing

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)

Page 42: Hashing

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−α )α

Page 43: Hashing

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

Page 44: Hashing

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

Page 45: Hashing

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();

}

Page 46: Hashing

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; }

Page 47: Hashing

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; }

Page 48: Hashing

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;

} }}

Page 49: Hashing

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;}

Page 50: Hashing

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

Page 51: Hashing

51

Prioritetskøer

Page 52: Hashing

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

Page 53: Hashing

53

Anvendelser af prioritetskøer

• operativsystemer

• grafsøgning

• datakomprimering

• diskret simulering

• sortering

Page 54: Hashing

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(); }

Page 55: Hashing

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]; }}

Page 56: Hashing

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--]; }

Page 57: Hashing

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();

Page 58: Hashing

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

Page 59: Hashing

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

Page 60: Hashing

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

Page 61: Hashing

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.

Page 62: Hashing

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

Page 63: Hashing

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.

Page 64: Hashing

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;}

Page 65: Hashing

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

Page 66: Hashing

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.

Page 67: Hashing

67

Kompleksitet:

O( i=2..N log(i)) = O(N log N)

for (int i = 2; i <= N; i++) percolateUp(i);

Page 68: Hashing

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

Page 69: Hashing

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); } }

Page 70: Hashing

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

Page 71: Hashing

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

Page 72: Hashing

72

Animering af heapsort(hob-konstruktions-fase)

Page 73: Hashing

73

Animering af heapsort(sorteringsfase)

Page 74: Hashing

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

Page 75: Hashing

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.

Page 76: Hashing

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.

Page 77: Hashing

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

Page 78: Hashing

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.

Page 79: Hashing

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.

Page 80: Hashing

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.

Page 81: Hashing

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

Page 82: Hashing

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.

Page 83: Hashing

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