Hashing

Post on 06-Jan-2016

64 views 0 download

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

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