Hashtabeller! (& kanske lite sortering)

Relevanta dokument
Övning 4. Hashning, sortering, prioritetskö, bästaförstsökning. Hitta på en perfekt hashfunktion för atomer. Hur stor blir hashtabellen?

DD1320 Tillämpad datalogi. Lösning (skiss) till tenta 20 okt 2011

Föreläsning 7: Prioritetskö, trappa heapsort, hashning

DD1320 Tillämpad datalogi. Lösnings-skiss till tentamen

Magnus Nielsen, IDA, Linköpings universitet

Datastrukturer och algoritmer. Föreläsning 15 Inför tentamen

Tommy Färnqvist, IDA, Linköpings universitet. 1 ADT Map/Dictionary Definitioner Implementation... 2

3. Toppkvinnor på hög Låt lådan och de två kvinnornas famnar utgöra stackarna L, K1 respektive K2. Från början finns alla kort i L.

Grundläggande datalogi - Övning 4

Python. Datatyper. Mer om datatyper. Heltal - 3 Flyttal - 2,456 Listor - [1,2,3,4] Strängar - spam!

Fredag 10 juni 2016 kl 8 12

Avbildningar och hashtabeller. Koffman & Wolfgang kapitel 7, mestadels avsnitt 2 4

Tildatenta Lösningsskiss

Tommy Färnqvist, IDA, Linköpings universitet. 1 ADT Map/Dictionary Definitioner Implementation... 2

Datastrukturer. föreläsning 6. Maps 1

Datastrukturer och algoritmer. Innehåll. Tabell. Tabell - exempel. Gränsyta till Tabell. Tabell. Modell. Hashtabell Relation, lexikon.

Föreläsning 8. Mängd, Avbildning, Hashtabell

KTH, NADA, Vahid Mosavat. 1. Flervalsfrågor (5p)

Programkonstruktion och. Datastrukturer

Tentamen med lösningsförslag Datastrukturer för D2 DAT 035

Föreläsning 8. Mängd, Avbildning, Hashtabell

Tentamen kl Uppgift 4. Uppgift 5

Föreläsningsanteckningar, Introduktion till datavetenskap HT S4 Datastrukturer. Tobias Wrigstad

Datalogi för E Övning 3

Programmering för språkteknologer II, HT2014. Rum

Innehåll. F7: Tabell, hashtabell, relation & lexikon. Gränsyta till Tabell. Tabell. Tabell Hashtabell Relation Lexikon.

Lösningsförslag DD1320/DD

Sökning. Översikt. Binärt sökträd. Linjär sökning. Binär sökning. Sorterad array. Linjär sökning. Binär sökning Hashtabeller

Datastrukturer, algoritmer och programkonstruktion (DVA104, VT 2015) Föreläsning 6

Lösningar Datastrukturer TDA

Hashing Bakom kulisserna på Pythons dictionary

Grundläggande Datalogi för F

Hashing Bakom kulisserna på Pythons dictionary

Hashing Bakom kulisserna på Pythons dictionary. Leta i listor Osorterade listor. Leta i listor Sorterade listor

Hashtabeller. TDA416, lp3 2016

Innehåll. Föreläsning 12. Binärt sökträd. Binära sökträd. Flervägs sökträd. Balanserade binära sökträd. Sökträd Sökning. Sökning och Sökträd

Tentamen Datastrukturer för D2 DAT 035

Föreläsning 4 Datastrukturer (DAT037)

Innehåll. Föreläsning 10. Specifikation. Mängd. Specifikation. Konstruktion av mängd. Mängd Lexikon Hashtabell

Diskutera. Hashfunktion

TDDE44 Programmering, grundkurs

Grundläggande Datalogi för F

DAI2 (TIDAL) + I2 (TKIEK)

Föreläsning 18 Filer och avbildningar

Övningsuppgifter #11, Programkonstruktion och datastrukturer

Föreläsning 10 Innehåll

Lösningsförslag för tentamen i Datastrukturer (DAT037) från

Övning 7 - Tillämpad datalogi DD1320, TENTAMEN I TILLÄMPAD DATALOGI Tisdagen den 12 januari 2010 kl 14 18

Sökning i ordnad lista. Sökning och sortering. Sökning med vaktpost i oordnad lista

Inlämningsuppgift och handledning

Inlämningsuppgift : Finn. 2D1418 Språkteknologi. Christoffer Sabel E-post: csabel@kth.se 1

Inlämningsuppgift och handledning. Föreläsning 11 Innehåll. Diskutera. Hashtabeller

Föreläsning 10 Innehåll. Diskutera. Hashtabeller. Hashfunktion. hashfunktion. hashkod (ett heltal)

Föreläsning 6 Datastrukturer (DAT037)

Föreläsning 11 Innehåll

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

Övning 4 - Tillämpad datalogi 2012

Introduktion till programmering D0009E. Föreläsning 9: Tupler och dictionaries

Programmering för språkteknologer II, HT2011. Rum

Hitta k största bland n element. Föreläsning 13 Innehåll. Histogramproblemet

Träd Hierarkiska strukturer

Tenta i Grundläggande programmering DD klockan

ADT Prioritetskö. Föreläsning 13 Innehåll. Prioritetskö vs FIFO-kö. Prioritetskö Exempel på användning. Prioritetsköer och heapar

Sökning och sortering

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 9 Jonas Lindgren, Institutionen för Datavetenskap, LiU

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Tommy Färnqvist, IDA, Linköpings universitet. 1 Administrativ information Upplägg... 1

Föreläsning 10 Innehåll. Prioritetsköer och heapar. ADT Prioritetskö. Interface för Prioritetskö. Exempel på vad du ska kunna

Ordlistor, filhantering och ut på webben. Linda Mannila

Föreläsning 5 Datastrukturer (DAT037)

Övning 4 - Tillämpad datalogi 2013

Interfacen Set och Map, hashtabeller

Algoritmer och effektivitet. Föreläsning 5 Innehåll. Analys av algoritmer. Analys av algoritmer Tidskomplexitet. Algoritmer och effektivitet

TENTAMEN I DATASTRUKTURER OCH ALGORITMER DVG B kl. 14:00-19:00

729G04 Programmering och diskret matematik. Föreläsning 7

Föreläsning 14 Innehåll

Tentamen i Algoritmer & Datastrukturer i Java

Planering av ett större program, del 2 - for och listor. Linda Mannila

Tentamen TEN1 HI

Tommy Färnqvist, IDA, Linköpings universitet

Föreläsning Datastrukturer (DAT037)

Exempeltenta GruDat 2002/2003

Tentamen (del 2) (4 högskolepoäng) i Programkonstruktion och datastrukturer (1DL201)

Inom datalogin brukar man använda träd för att beskriva vissa typer av problem. Om man begränsar sig till träd där varje nod förgrenar sig högst två

Föreläsning 5 Innehåll

Tentamen TEN1 HI

Föreläsning Datastrukturer (DAT037)

Föreläsning REPETITION & EXTENTA

Föreläsning 4 Datastrukturer (DAT037)

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

Föreläsning 9 Innehåll

Vad har vi pratat om i kursen?

Föreläsning 10 Datastrukturer (DAT037)

729G74 IT och programmering, grundkurs. Tema 3. Föreläsning 2 Jody Foo,

Prov i DAT 312: Algoritmer och datastrukturer för systemvetare

Föreläsning 9: Talteori

Föreläsning Datastrukturer (DAT036)

ADT Prioritetskö. Föreläsning 12 Innehåll. Prioritetskö. Interface för Prioritetskö. Prioritetsköer och heapar

Transkript:

Datalogiövning 24/1 2007 Hashtabeller! (& kanske lite sortering) Allmänt om hashtabeller: Snabb lösning för sökningar, O(1). Man lagrar par av nycklar och värden. En hashfunktion beräknar ur nyckeln ett hashtabell-index där värdet ska lagras. När man sedan vill hitta värdet för en viss nyckel så gör man ungefär samma sak; man beräknar hash-värdet för nyckeln och letar på den plats i tabellen som man får ut. En perfekt hashfunktion genererar olika värden för alla möjliga nycklar; en minimal perfekt hashfunktion använder dessutom alla index i tabellen. Sådana hashfunktioner finns, men de behövs oftast inte, dessutom kräver de att man känner till nycklarna i förväg och att storleken på dessa är begränsad. I praktiken räcker det att man får bra spridning på värdena. Inget entydigt svar på vad som är en "bra" hashfunktion. Generellt sett vill man ha en funktion som sprider ut värdena nästan slumpmässigt. Det är också bra om hashfunktionen går fort att beräkna. Här använder vi oss av enkla hashfunktioner som försöker sätta ett unikt hash-värde på varje möjligt input; sedan använder man modulo för att få det att rymmas i tabellen. Om man bara ska sätta in heltal så behöver man ingen speciell hashfunktion; talen är ju i sig bra index till hashtabellen. En stor hashtabell ger färre krockar men tar upp mer minne. Man vill ha en kompromiss. En tumregel är att göra hashtabellen dubbelt så stor som antalet element man vill sätta in ("50 % luft"). Man använder ofta modulo-operatorn för att få fram ett index som ligger inom tabellens gränser. Det har visat sig att spridningen blir bäst om man använder ett primtal som storlek på tabellen. Krockar kan hanteras på olika sätt, t ex med krocklistor eller linjär/kvadratisk probning. (Linjär kan leda till problem med klustring, klumpar av värden som gör det svårt att hitta en ledig plats.) Man kan också använda omhashning, dvs använda en annan hashfunktion; antingen för att hitta antalet steg som man ska hoppa i tabellen eller för att lägga in i ytterligare en hashtabell. Hashtabeller har alltid O(1) i bästa fallet, men olika krockhanteringsmetoder ger olika värstafallskomplexitet. Krocklistor har t ex i värsta fallet O(N) om man använder just listor (varför?). Men om man har ett självbalanserande binärt träd istället för en krocklista får man O(log N). Krocklistor är okänsliga för klustring, medan probing-baserade metoder är mer beroende av en bra hashfunktion. Bloomfilter: Man kan göra en typ av minnessnål multipel hashning som heter Bloom-filter. I t ex ett stavningskontrollprogram där man måste kunna ha med massor av ord vill vi att det ska vara minnessnålt och vill alltså inte lagra själva orden. Vi kan då ha en boolsk hashtabell som bara lagrar ettor och nollor. Tabellen är initialiserad så att den är full av nollor. Sedan beräknar man hashfunktionen för alla rättstavade ord och sätter ettor på motsvarande plats i tabellen. Då får vi perfekt "recall" av rättstavade ord, dvs om vi har stavat rätt så kommer programmet alltid att veta detta. Det finns dock ett problem med falska positiva: om ett felstavat ord råkar få samma hashkod som ett rättstavat, så kommer ordet inte att markeras som felstavat. För att råda bot på detta kan man använda flera olika hashfunktioner (t ex 14) och ordet måste få ettor efter att ha körts genom samtliga. Då är det mycket liten chans att felstavade ord slinker igenom. Detta lägger bara till en konstant faktor och påverkar inte komplexiteten, som fortfarande är O(1). I Python finns en datatyp som heter dictionary och motsvarar en hashtabell. Den lagrar alltså par av nycklar och värden utan någon särskild inbördes ordning. Nycklar kan vara t ex tal eller strängar (men inte listor). >>>d = {} # Skapar en tom dictionary >>>d={'sverige':'stockholm','finland':'helsingfors'} # Skapar en dictionary med två nyckel/värde-par Nu kan man skriva t ex

>>>d['finland'] 'Helsingfors' Om man försöker lägga in ett nytt värde för en nyckel som redan finns, så skrivs det gamla värdet över. Man kan alltså inte ha fler värden för en nyckel. Dictionaries kan växa och krympa medan programmet körs. Man kan ta ut nycklar och värden genom att använda del: >>> del d['finland'] Man kan skriva ut alla nycklar med keys(), kanske i kombination med sort(). >>> d.keys() ['Finland', 'Sverige'] Värdena kan på samma sätt skrivas ut med values(). >>> d.values() ['Helsingfors', 'Stockholm'] Om man gör en for-loop över en dictionary på det lättaste sättet (for i in d:) så loopar man över nycklarna. Det finns också en funktion items() som skriver ut nyckel/värde-paren som tupler, en annan datastruktur i Python. >>> d.items() [('Finland', 'Helsingfors'), ('Sverige', 'Stockholm')] Man kan kolla om nycklar eller värden finns: >>> d.has_key('norge') False >>> d.has_key('finland') True Ett exempel ur boken: Om man jobbar med stora matriser där många element är noll så kan det vara bra att använda en dictionary istället. (OK, den här är inte så stor, men man skulle kunna tänka sig t ex en sökmotor som håller reda på vilka ord som finns på vilka webbsidor; då skulle man få väldigt många nollor!) Matrisen ovan kan man skriva som en dictionary där man bara tar med de nollskilda elementen: matrix = {(0,3): 1, (2, 1): 2, (4, 3): 3} Här är nycklarna alltså tupler. Man kan nu skriva t ex >>> matrix[(4,3)] 3 Tyvärr så får man nu ett felmeddelande om man försöker komma åt ett nollelement: >>> matrix[(4,4)] Traceback (most recent call last): File "<stdin>", line 1, in?

KeyError: (4, 4) Det måste man gå runt genom att använda metoden get() för dictionaries; den hämtar värdet om nyckeln finns, eller returnerar ett default-värde annars. Detta default-värde blir noll i vårt fall. >>> matrix.get((4,4),0) 0 >>> matrix.get((4,3),0) 3 Hashtabeller/dictionaries kan med fördel användas istället för dumträd i BFS/DFS. Allmänna uppgifter på hashtabeller Under gulfkriget var det väldigt svårt för armestaben att hålla reda på alla TV-bolag som for omkring och rapporterade i öknen. För att hålla reda på dem användes en hashvektor. Koden fungerade inte som avsett och man har nu gett i uppdrag åt en f.d. datalogistudent att titta på en misstänkt del av koden: hashvector = [None]*100 def put(s, val): hashcode = 0 for l in s: hashcode += ord(l) hashcode = hashcode % 100 hashvector[hashcode] = val Vad är det för fel på koden? Beskriv hur man kan förbättra den. Namnen på TV-bolagen kan antas bestå av högst tre bokstäver. Det kommer inte att förekomma mer än 75 TV-bolag. 4 förbättringar; ett direkt fel, två prestandasänkande saker och en ett exempel på dålig programmeringsstil. Det som gör att koden inte fungerar är att det inte finns någon krockhantering. hashvektor[hashcode] = info; För att lösa det kan man använda t.ex. krocklistor eller linjär sökning. Linjär sökning fungerar så att om det är upptaget på den givna positionen så söker man tills man antingen hittar nyckeln (den var redan instoppad sedan tidigare) eller tills man hittar ett tomt element i vektorn. Möjlig kod för krocklista: hashvector = [None]*100 def put(s, val): hashcode = 0 for l in s: hashcode += ord(l)

hashcode = hashcode % 100 if hashvector[hashcode]==none: hashvector[hashcode]=[val] else: hashvector[hashcode].append(val) Hashkoden är ganska korkat implementerad. Strängarna "AB" och "BA" får samma värde. För att lösa det kan man vikta genom att multiplicera med t.ex. 1, 100 och 10000. Längden på hashvektorn är lite för liten och dessutom inget primtal. Välj istället hashvektorns storlek till exempelvis 151 (dubbel så stor som förväntade antalet element). Det är dessutom dålig programmeringsstil att hårdkoda värdet på flera ställen, använd en variabel istället. Databasprogram Till en databas med c:a hundra tusen historiska händelser under dom senaste femtusen åren ska en hashvektor skapas för snabb sökning efter ett givet datum. Föreslå en bra hashfunktion. En generell hashfunktion är h(key) = key % size Då måste du hitta en lämplig tabellstorlek (size) och nyckel (key) för att hitta/lägga in information i hashtabellen. Det finns 100 000 händelser att hasha. Hashvektorn bör ha primtalsstorlek som ger 50% luft, d v s runt 200 000. Anta att alla år innan 0 E.Kr. är negativa tal. En lämplig nyckel är till exempel (50000000 - YYYYMMDD). Man tar bort datumet från 50000000 för att undvika negativa tal. Då blir hashfunktionen Kollisioner h(key) = (50000000 - YYYYMMDD) % 200003 Redogör för kollisionshantering i hashtabeller. Måste man alltid hantera krockar? Vad händer om man inte gör det? Vad innebär klustring? Hur fungerar kollisionshantering i bloomfilter? Arkens djurpar I arken fanns två individer av varje art, alltså herr och fru marsvin, herr och fru gråsugga osv. Noa har ordentligt fört in varje individs artnamn i en fil, dock huller om buller. Det finns cirka två miljoner namn i filen och Noa behöver hjälp med att kolla att varje namn förekommer exakt två gånger. Uppskatta komplexiteten (antal jämförelser) för följande metoder. Djuren in i en kö, upprepa sedan följande: Första djuret ut, snurra igenom kön och ta bort maken.

Djuren in i vektor som quicksorteras och sedan kollas igenom (hur?). Djuren in i ett binärträd som sedan kollas igenom (hur?). Djuren in i en hashtabell med cirka tre miljoner platser som sedan kollas igenom (hur?). Djuren in i trappa (heap) som sedan töms parvis. Djuren hashas in i Viggos bloomfilter med fjorton hashfunktioner. Om någon metod är helt oanvändbar ska du tala om det. Uppfinn gärna någon egen, ännu bättre metod! Kömetoden kräver en biljon jämförelser, bloomfiltret är oanvändbart. Quicksortering följd av parkoll tar N log N + N/2 jämförelser. Binärträd med inordergenomgång tar cirka 2NlogN, trappa likaså. Hashningen går i linjär tid, t ex 5N och parkollen tar cirka 2N. Klart snabbast alltså eftersom i detta fall log N blir ca 20. Ännu något effektivare är att redan vid inhashningen observera om det finns fler än två av någon art. ----------- 041019:4 Fågelsökning Lilla fågelboken, med hundra uppslagsord, och Stora fågelboken, med tiotusen uppslagsord finns lagrade på datorsökbar form. Det finns två olika sätt att söka på. Sökmetod A tar lika lång tid för bägge fågelböckerna, men sökmetod B tar dubbelt så lång tid när man söker i Stora fågelboken. (4p) Vilka är sökmetoderna? Motivera ditt svar! Sökmetod A, som tar lika lång tid oberoende av antal uppslagsord är hashning, som är O(1). Sökmetod B tar dubbelt så lång tid när antalet uppslagsord ökar kvadratiskt (1002 = 10000) och är alltså O(logn), vilket innebär binärsökning (i sorterad array eller binärt sökträd). Unika Pokémonkort Linda har n stycken pokémonkort hemma. Varje gång hon får nya kort skriver hon in namnen sist i en fil. Nu vill hon skapa en lista med unika kort, alltså utan eventuella dubbletter. Uppskatta komplexiteten (antal jämförelser) för följande metoder. Ange också vilka metoder som är oanvändbara, eller behöver modifieras. Uppfinn gärna någon egen, ännu bättre metod! 1. Läs in korten till en array, upprepa sedan följande för varje kort: Skriv ut kortet, sök sedan med linjärsökning efter dubbletter i resten av arrayen, och sätt dessa till null. 2. Läs in korten till en array. Sortera korten i bokstavsordning med insättningssortering. Skriv sen ut alla kort som inte är identiska med nästa i vektorn. 3. Hasha in korten i en hashtabell utan krocklistor. Skriv sedan ut alla nullskilda element. 4. Hasha in korten i Viggos bloomfilter med fjorton hashfunktioner. Gå sedan igenom filen igen och skriv ut alla kort som ger trä i bloomfiltret. 5. Sortera in korten i ett binärt sökträd, som i labb 3, och skriv sedan ut trädet i inorder.

6. Läs in korten till en stack. Upprepa följande till stacken är tom: Poppa första kortet och skriv ut det, poppa sedan resten av korten, utom dom som är identiska med första kortet, till en andra stack och poppa tillbaka rubbet till första stacken. 1. Läs in korten till en array, upprepa sedan följande för varje kort: Skriv ut kortet, sök sedan med linjärsökning efter dubbletter i resten av arrayen, och sätt dessa till null. Inläsning O(n), en linjärsökning för varje kort no(n), utskrift O(n)=>O(n2 ) totalt. Fungerar. 2. Läs in korten till en array. Sortera korten i bokstavsordning med insättningssortering. Skriv sen ut alla kort som inte är identiska med nästa i vektorn. Inläsning O(n), sortering n2, sen går man igenom alla igen och jämför med nästa vilket tar O(n) =>O(n2 ) totalt. Fungerar i princip, men vi måste lägga till utskrift av allra sista elementet. 3. Hasha in korten i en hashtabell utan krocklistor. Skriv sedan ut alla nullskilda element. Att hasha in alla korten tar O(n) och utskriften O(n) =>O(n) totalt. Men det fungerar bara om man har en perfekt hashfunktion, annars kan man ju få krockar även för olika kort. 4. Hasha in korten i Viggos bloomfilter med fjorton hashfunktioner. Gå sedan igenom filen igen och skriv ut alla kort som ger trä i bloomfiltret. Komplexitet O(n) som hashningen ovan, men fungerar inte. Eftersom vi går igenom filen när vi ska skriva ut korten och inte sållar bort några kort så kommer alla kort att skrivas ut. 5. Sortera in korten i ett binärt sökträd, som i labb 3, och skriv sedan ut trädet i inorder. Stoppa in n kort i trädet tar no(logn), skriva ut tar O(n) => O(nlogn) totalt. Fungerar bra om man i insättningmetoden låter bli att lägga in ett kort om man upptäcker att det redan fanns med. 6. Läs in korten till en stack. Upprepa följande till stacken är tom: Poppa första kortet och skriv ut det, poppa sedan resten av korten, utom dom som är identiska med första kortet, till en andra stack och poppa tillbaka rubbet till första stacken. Första kortet jämförs med resten (som vid urvalssortering) =>O(n2 ) totalt. Fungerar om man när man poppar till den andra stacken slänger bort alla kort som är identiska med det första.