Grundläggande datalogi - Övning 4 Björn Terelius November 21, 2008
Definitioner Olika mått på komplexitet Definition En funktion f sägs vara O(g) om det existerar konstanter c, N så att f (n) < cg(n) för alla n > N. Detta skrivs f O(g) eller (något oegentligt) f = O(g). Exempel 3n 2 + 4n + 5 O(n 2 ) 7n log n + 103n + 9 O(n log n) 5 log n + 1 O(log n) 5 log n + 1 O(n)
Definitioner Olika mått på komplexitet Tidskomplexitet Hur många enkla* operationer utförs? Ger en uppskatting av hur lång tid algoritmen kommer ta Oftast det mest intressanta komplexitetsmåttet Minneskomplexitet Hur mycket minne använder algoritmen? De flesta algoritmer använder inte så mycket minne,...... men om minnet tar slut kan vi inte fortsätta beräkningen *En operation anses här vara enkel om den tar konstant tid.
Definitioner Olika mått på komplexitet Värsta fallet Hur många operationer/lång tid kommer som mest behövas Ofta ganska lätt att beräkna Kan vara onödigt pessimistiskt Genomsnittligt fall Ger oftast en bättre uppskattning av hur lång tid algoritmen kommer ta i praktiken,...... men vissa indata kan kräva mycket mer systemresurser Svårare att beräkna än komplexiteten för värsta fallet Kräver att man har sannolikhetsfördelningen för indata
Definitioner Olika mått på komplexitet Hur beräknar man komplexiteten för en algoritm? Loopar: Hur många varv körs loopen? Hur mycket arbete utförs per varv? if-else satser: Vilket fall kräver mest arbete?
Definitioner Olika mått på komplexitet insert find/exists remove oordnad lista O(1) O(n) O(n) ordnad lista O(n) O(log n) O(n) binärträd* O(log n) O(log n) O(log n) hashtabell** O(1) O(1) O(1) *Komplexiteten för binärträdet gäller under antagandet att det är balanserat **Komplexiteten för hashtabellen är förväntad tid, värsta-fallet kan vara O(n)
Vi kan hitta ett element i en lista i tid O(1) om vi vet dess index. Idé: Antag att vi snabbt kan associera ett index till varje nyckel. Då kan vi lagra nyckel och värde på den positionen i en vanlig lista. Problem: Hur tilldelar vi ett index till t.ex. en sträng eller ett heltal? Vad gör vi om/när flera nycklar tilldelas samma index?
Problem: Om vi inte känner till vilka nycklar som kommer sättas in när vi designar hashfunktionen kommer vi med stor sannolikhet få kollisioner. Lösningar: Lagra alla nycklar som hashar till samma värde i en länkad lista Hitta ett nytt index i tabellen, t.ex nästa lediga index
En hashfunktion är en funktion från någon mängd nycklar till talen 0, 1,..., N 1 där N är storleken på tabellen. En ideal hashfunktion sprider nycklarna oberoende och likformigt bland 0, 1,..., N 1. I realiteten får man göra en kompromiss mellan att hashfunktionen ska vara snabb att beräkna och att den ska sprida nycklarna bra. För heltal kan en enkel hashfunktion skrivas def hash(i, size): Given an integer i, the function returns a hash value in the range [0, size-1] return i % size
def hash(str, size): sum = 0 for c in str: sum += ord(c) return sum % size Lätt att förstå och implementera Snabb att köra Klustrar strängar mycket kraftigt Byter man plats på två bokstäver ändras inte hashvärdet
def hash(str, size): h = 0 for c in str: h = (127 * h + ord(c)) % size return h Mycket bättre spridning av nycklarna Något långsammare Fortfarande dålig om tabellstorleken är en multipel av 127 Ofta väljer man att tabellstorleken ska vara ett primtal.
from string_hash import hash class HashTableNode: def init (self, k, v, n): self.key = k self.value = v self.next = n class HashTable: def init (self, size): self.table = [None]*size...
def insert(self, key, value): Inserts the key/value pair in the hashtable h = hash(key, len(self.table)) ptr = self.table[h] while ptr!=none: if ptr.key == key: ptr.value = value return ptr = ptr.next n = HashTableNode(key, value, self.table[h]) self.table[h] = n
def exists(self, key): Returns True if the key exists, False otherwise h = hash(key, len(self.table)) ptr = self.table[h] while ptr!=none: if ptr.key == key: return True ptr = ptr.next return False
def get(self, key): Returns the value associated with key if the key exists, None otherwise h = hash(key, len(self.table)) ptr = self.table[h] while ptr!=none: if ptr.key == key: return ptr.value ptr = ptr.next return None
def remove(self, key): Removes the given key from the hashtable Returns True if it existed, False otherwise h = hash(key, len(self.table)) ptr = self.table[h] if ptr!=none and ptr.key==key: self.table[h] = ptr.next return True while ptr.next!=none and ptr.next.key!=key: ptr = ptr.next if ptr.next!=none and ptr.next.key==key: ptr.next = ptr.next.next return True return False
>>> ages = HashTable(20) >>> ages.insert("homer", 38) >>> ages.insert("marge", 37) >>> ages.insert("lisa", 8) >>> ages.insert("maggie", 1) >>> ages.insert("bart", 11) >>> ages.exists("lisa") True >>> ages.exists("spider pig") False >>> ages.get("homer") 38 >>> ages.remove("lisa") True >>> ages.exists("lisa") False