Advanced Data Indexing (Προηγμένη ευρετηρίαση δεδομένων) Κατακερματισμός - Hashing (2 ο Μέρος)
Μέθοδοι Κατακερματισμού Direct Addressing Hashing with chaining Hashing by Division Hashing by Multiplication Universal Hashing Perfect Hashing (static, dynamic) integer universe random probing Hashing with chaining Hashing with open addressing Linear probing Quadratic probing Double Hashing Brent’s Method Multiple-Choice Hashing Asymmetric Hashing LCFS Hashing Robin-Hood Hashing Cuckoo Hashing Bloom Filters
Ανοικτή Διευθυνσιοδότηση (Hashing with Open Addressing)
4 Βασική διαφορά από την μέθοδο κατακερματισμού με αλυσίδες είναι ότι σε κάθε θέση του πίνακα hash αποθηκεύεται μόνο ένα στοιχείο. Υπάρχει μία προκαθορισμένη ακολουθία ελέγχων η οποία αναλαμβάνει την διαχείριση των συγκρούσεων μέσα στον πίνακα. Βασική ιδέα είναι πως όταν γίνεται μία σύγκρουση δοκιμάζεται η επόμενη θέση που καθορίζει η ακολουθία ελέγχων. Το κόστος αναζήτησης, εισαγωγής και διαγραφής είναι ανάλογο με το πλήθος των ελέγχων που απαιτούνται.
5 Ανοικτή Διευθυνσιοδότηση (Hashing with Open Addressing) Έστω A[x 0 ], A[x 1 ], A[x 2 ], … η προκαθορισμένη ακολουθία των θέσεων που ελέγχονται για να γίνει κάποια βασική πράξη που αφορά ένα στοιχείο x. Τότε: Για τη διαγραφή του x: Γίνεται αναζήτηση του στοιχείου με την κατάλληλη ακολουθία ελέγχων. Αν εντοπιστεί στη θέση A[x i ] τότε σημειώνεται ως διαγραμμένο. Η σημείωσή του γίνεται: είτε με ενεργοποίηση ενός extra bit (αν υπάρχει στις θέσεις του πίνακα hash). είτε με την εισαγωγή μίας προκαθορισμένης ειδικής τιμής-κλειδιού στη θέση του, που θα δείχνει ότι το στοιχείο στη θέση αυτή έχει διαγραφεί (del).
6 Ανοικτή Διευθυνσιοδότηση (Hashing with Open Addressing) Για την εισαγωγή του x: Ελέγχονται οι θέσεις τοποθέτησης με την κατάλληλη ακολουθία ελέγχων. Αν η θέση A[x i ] είναι άδεια ή έχει κάποιο στοιχείο που έχει προσημειωθεί ως διαγραμμένο τότε το x εισάγεται στη θέση αυτή. Για την αναζήτηση του x: Γίνεται αναζήτηση του στοιχείου x με την κατάλληλη ακολουθία ελέγχων. Αν εντοπιστεί στη θέση A[x i ] τότε υπάρχει στον πίνακα και επιστρέφεται η θέση αυτή. Αν εντοπιστεί στην ακολουθία μία τελείως κενή θέση (προσοχή: όχι στοιχείο που προσημειώθηκε ως διαγραμμένο) τότε το x σίγουρα δεν είναι αποθηκευμένο στον πίνακα.
7 Παράδειγμα Έστω ότι η ακολουθία ελέγχων γίνεται στις θέσεις 1, 5, 9, … κατά σειρά. Εισαγωγή του 14: Η θέση 1 είναι κατειλημμένη από το 79. Επόμενος έλεγχος η θέση 5. Η θέση 5 είναι κατειλημμένη από το 98. Επόμενος έλεγχος η θέση 9. Η θέση 9 είναι άδεια και γίνεται η τοποθέτηση. Διαγραφή του 98: Η θέση 1 δεν περιέχει το 98. Επόμενος έλεγχος η θέση 5. Η θέση 5 έχει το 98 και σημειώνεται ως διαγραμμένο. Αναζήτηση του 14: Η θέση 1 είναι κατειλημμένη από το 79. Επόμενος έλεγχος η θέση 5. Η θέση 5 περιέχει διαγραμμένο στοιχείο. Επόμενος έλεγχος η θέση 9. Η θέση 9 έχει το ζητούμενο στοιχείο.
Γενίκευση Συνάρτησης Κατακερματισμού Η ακολουθία ελέγχων για τις θέσεις του πίνακα πρέπει να καθορίζεται από την ίδια την συνάρτηση hash. Αυτό σημαίνει ότι η συνάρτηση αυτή θα έχει πλέον 2 παραμέτρους: (i) Το Στοιχείο/Κλειδί x, και (ii) την παράμετρο της ακολουθίας ελέγχων p h(x,p), p=0,1,...,m-1 Τότε η ακολουθία ελέγχων θα είναι η σειρά: Για να καλύπτει όλες τις θέσεις του πίνακα πρέπει να είναι αναδιάταξη της σειράς: Υπάρχουν m! διαφορετικές αναδιατάξεις. Οι καλές οικογένειες συναρτήσεων αναπαράγουν όλες τις m! αναδιατάξεις.
9 Ανοικτή Διευθυνσιοδότηση - Απόδοση Έστω A[x 0 ], A[x 1 ], A[x 2 ],…, A[x i ] η προκαθορισμένη ακολουθία των θέσεων που ελέγχονται για να γίνει οποιαδήποτε βασική πράξη (search, insert, delete) που αφορά ένα στοιχείο x, μέχρι την τελευταία θέση που απαιτείται, την A[x i ], δηλαδή μετά από i ελέγχους. Έστω επίσης λ η πιθανότητα ώστε μία θέση του πίνακα να είναι κατειλημμένη (ίδια πιθανότητα για όλες τις θέσεις). Επειδή οι θέσεις είναι ανεξάρτητες, η συνολική πιθανότητα για την πράξη θα είναι:
10 Ανοικτή Διευθυνσιοδότηση - Απόδοση Συνεπώς το αναμενόμενο κόστος της πράξης θα είναι: Δηλαδή κάθε πράξη (search, insert, delete) απαιτεί κόστος Θ(1/(1-λ)) και έτσι είναι απόλυτα εξαρτημένο από την τιμή λ. Προσοχή: Όταν γίνονται έντονες μεταβολές στον πίνακα (π.χ. μαζικές εισαγωγές ή διαγραφές τότε η πιθανότητα σύγκρουσης λ μπορεί να μεταβληθεί σημαντικά. Αποδεικνύεται ότι το κόστος της χειρότερης περίπτωσης για την κάθε πράξη είναι O(logn).
Μέθοδοι Ανοικτής Διευθυνσιοδότησης Ανάλογα με τον τρόπο που καθορίζεται η ακολουθία ελέγχων (μέσω της συνάρτησης hash) προκύπτει και διαφορετική απόδοση για τις βασικές πράξεις. Κοινές μέθοδοι είναι: Γραμμικός Έλεγχος (Linear Probing) Τετραγωνικός Έλεγχος (Quadratic Probing) Διπλός Κατακερματισμός (Double Hashing) Όμως καμία από αυτές δεν παράγει περισσότερες από m 2 αναδιατάξεις των m θέσεων (βέλτιστο: m!).
Γραμμικός Έλεγχος (Linear Probing)
Βασική Ιδέα: όταν υπάρχει σύγκρουση, βρίσκουμε την επόμενη διαθέσιμη θέση στον πίνακα ελέγχοντας τις θέσεις διαδοχικά μία προς μία. Έστω h 1 η βασική συνάρτηση hash που δίνει τις θέσεις στον πίνακα. Μπορεί να γίνει εκλογή οποιασδήποτε συνάρτησης Μία συνάρτηση που κάνει ομοιόμορφο κατακερματισμό (τυχαία διασπορά) θα έχει καλή απόδοση. Τότε η συνάρτηση: h(x,i) = (h 1 (x) + i) mod m, i=0,1,2,... θα καθορίζει την ακολουθία ελέγχων.
Γραμμικός Έλεγχος (Linear Probing) h(x,i) = (h 1 (x) + i) mod m i = 0,1,2,... Πρώτη θέση ελέγχου: h 1 (x) Δεύτερη θέση ελέγχου: h 1 (x) + 1 Τρίτη θέση ελέγχου: h 1 (x)+2, κοκ. Ακολουθία ελέγχων: Επιστρέφει στην αρχή
Παράδειγμα h 1 (x) = x mod 31, (m=31) h(x,i) = (h 1 (x) + i) mod 31, i=0,1,2,... Εισαγωγή κατά σειρά των 126,64,95,33,100: Επειδή 126 = 2 mod 31, το 126 εισάγεται κανονικά στη θέση 2. Επειδή 64 = 2 mod 31, και η θέση 2 είναι κατειλημμένη από το 126, η εισαγωγή του 64 γίνεται στην επόμενη θέση 3. Επειδή 95 = 2 mod 31, και οι θέσεις 2,3 είναι κατειλημμένες από τα 126,64, η εισαγωγή του 95 γίνεται στην επόμενη θέση 4. Επειδή 33 = 2 mod 31, και οι θέσεις 2,3,4 είναι κατειλημμένες από τα 126,64,95, η εισαγωγή του 33 γίνεται στην επόμενη θέση 5. Επειδή 100 = 7 mod 31, το 100 εισάγεται κανονικά στη θέση …
Παράδειγμα h 1 (x) = x mod 31, (m=31) h(x,i) = (h 1 (x) + i) mod 31, i=0,1,2,... Διαγραφή του 95: Επειδή 95 = 2 mod 31, ελέγχεται η θέση 2. Δεν υπάρχει εκεί οπότε ελέγχονται οι επόμενες θέσεις με τη σειρά. Το 95 εντοπίζεται στη θέση 4 και σημειώνεται ως διαγραμμένο. Αναζήτηση του 33: Επειδή 33 = 2 mod 31, ελέγχεται η θέση 2. Δεν υπάρχει εκεί οπότε ελέγχονται οι επόμενες θέσεις με τη σειρά. Η 3 η θέση έχει το 64. Συνεχίζεται ο έλεγχος στην 4 η που έχει διαγραμμένο στοιχείο. Συνεχίζεται ο έλεγχος στην 5 η θέση και εντοπίζεται το …
Παράδειγμα h 1 (x) = x mod 31, (m=31) h(x,i) = (h 1 (x) + i) mod 31, i=0,1,2,... Αναζήτηση του 2: Επειδή 2 = 2 mod 31, ελέγχεται η θέση 2. Δεν υπάρχει εκεί οπότε ελέγχονται οι επόμενες θέσεις με τη σειρά. Η 3 η θέση έχει το 64. Συνεχίζεται ο έλεγχος στην 4 η που έχει διαγραμμένο στοιχείο. Συνεχίζεται ο έλεγχος στην 5 η θέση που έχει το 33. Συνεχίζεται ο έλεγχος στην 6 η θέση που είναι κενή. Άρα το 2 δεν υπάρχει στον πίνακα. Εισαγωγή του 2: Επειδή 2 = 2 mod 31, και οι θέσεις 2,3 είναι κατειλημμένες από τα 126,64, η εισαγωγή του 2 γίνεται στην επόμενη θέση 4 που είχε διαγραμμένο στοιχείο …
Γραμμικός Έλεγχος (Linear Probing) Μειονεκτήματα: Αναπαράγονται μόνο m αναδιατάξεις των m θέσεων (όχι όλες οι m! αναδιατάξεις). Ουσιαστικά είναι οι κυκλικές μεταθέσεις. Συγκεντρώνονται στοιχεία κατά ομάδες σε γειτονικά κελιά (primary clustering) αυξάνοντας την πιθανότητα σε μελλοντικές συγκρούσεις. Μάλιστα οι μεγάλες ομάδες έχουν την τάση (μεγαλύτερη πιθανότητα) να μεγαλώνουν ακόμα περισσότερο (rich get richer). Πλεονεκτήματα: Εύκολη υλοποίηση (απλότητα). Εκμεταλλεύεται σημαντικά την τοπικότητα εφόσον γειτονικά κελιά αποθηκεύονται στο ίδιο μπλοκ (λιγότερα cache misses).
Γραμμικός Έλεγχος - Κόστος Έστω λ η πιθανότητα να συμβεί μία σύγκρουση. Το κόστος κάθε πράξης (search, insert, delete) για ένα στοιχείο x εξαρτάται από το πλήθος ελέγχων που απαιτούνται όταν υπάρχει ή όταν δεν υπάρχει το στοιχείο x στον πίνακα. Το αναμενόμενο πλήθος ελέγχων για μία επιτυχή αναζήτηση είναι: Το αναμενόμενο πλήθος ελέγχων για μία ανεπιτυχής αναζήτηση είναι: Έτσι, καθώς γεμίζει ο πίνακας, όταν έχουμε λ>1/2, η απόδοση αρχίζει να μειώνεται γρήγορα.
Τετραγωνικός Έλεγχος (Quadratic Probing)
Βασική Ιδέα: όταν υπάρχει σύγκρουση, βρίσκουμε την επόμενη διαθέσιμη θέση στον πίνακα ελέγχοντας c 2 i 2 +c 1 i θέσεις πιο μπροστά, για i=0,1,2,... Για c 2 =0, c 1 =1 έχουμε τον γραμμικό έλεγχο. Τυπική περίπτωση είναι για c 2 =1, c 1 =0. Έστω h 1 μία συνάρτηση hash που δίνει τις αρχικές θέσεις στον πίνακα. Τότε η συνάρτηση: h(x,i) = (h 1 (x) + c 2 i 2 +c 1 i) mod m, i=0,1,2,... καθορίζει την ακολουθία ελέγχων.
Τετραγωνικός Έλεγχος (Quadratic Probing) Πρέπει να γίνει προσεκτική επιλογή των σταθερών c 1, c 2 ώστε η ακολουθία ελέγχων να μπορεί να καλύψει όλες τις θέσεις του πίνακα κατά την πλήρη ανάπτυξή της (δύσκολο αν λ>1/2). Επίσης, πρέπει το m να είναι πρώτος αριθμός. Μάλιστα στην τυπική περίπτωση [h(x,i) = (h 1 (x)+i 2 ) mod m] πρέπει το m να είναι πρώτος της μορφής 4k+3, k Z. Αναπαράγονται μόνο m αναδιατάξεις των m θέσεων (όχι m!), καθώς η αρχική θέση που καθορίζει η συνάρτηση h 1 καθορίζει και την υπόλοιπη ακολουθία ελέγχων.
Παράδειγμα 1 ο h 1 (x) = x mod 7, h(x,i) = (h 1 (x) + i 2 ) mod 7, i=0,1,2,... 7=4 1+3 έλεγχοι: insert(76) 76mod7 = insert(40) 40mod7 = insert(48) 48mod7 = insert(5) 5mod7 = insert(55) 55mod7 = Όλα φαίνονται φυσιολογικά!
Παράδειγμα 2 ο h 1 (x) = x mod 7, h(x,i) = (h 1 (x) + i 2 ) mod 7, i=0,1,2,... 7=4 1+3 έλεγχοι: insert(76) 76mod7 = insert(47) 47mod7 = 5 insert(93) 93mod7 = insert(40) 40mod7 = insert(35) 35mod7 = Η ακολουθία ελέγχων δίνει τις θέσεις 5,6,2,0,0,2,6,5,6,2,0,0,2,6,5,….. Ποτέ δεν θα πέσουμε σε άδεια θέση!
Τετραγωνικός Έλεγχος (Quadratic Probing) h(x,i) = (h 1 (x)+i 2 ) mod m Θεώρημα: Αν λ 1/2 και m πρώτος τότε πάντοτε θα βρίσκεται άδεια θέση για την εισαγωγή στοιχείου. Απόδειξη: Για κάθε ζεύγος 0 i,j m/2 με i≠j είναι h(x,i)≠h(x,j) συνεπώς με m/2 το πολύ ελέγχους θα βρεθεί άδεια θέση. Απαγωγή σε άτοπο: Έστω αντίθετα ότι h(x,i)=h(x,j). Τότε: (h 1 (x) + i 2 ) mod m = (h 1 (x) + j 2 ) mod m i 2 mod m = j 2 mod m (i 2 - j 2 ) mod m = 0 [(i + j)(i - j)] mod m = 0 i + j = 0,m ή i - j = 0,m (άτοπο καθώς i≠j και i,j m/2)
Τετραγωνικός Έλεγχος - Προβλήματα Αν λ>1/2 τότε δεν υπάρχει εγγύηση ότι η ακολουθία ελέγχων θα πέσει σε άδεια θέση ώστε να εξασφαλιστεί η εισαγωγή στοιχείου (αποτυχία εισαγωγής). Συνεπώς πρέπει να μην αφήσουμε τον πίνακα να γεμίσει με στοιχεία πάνω από 50%. Ο τετραγωνικός έλεγχος δεν κινδυνεύει από πρόωρη συγκέντρωση στοιχείων (primary clustering), όπως συμβαίνει με τον γραμμικό, αλλά κινδυνεύει από επαγόμενη συγκέντρωση δηλαδή όταν έχουμε συγκρούσεις στην κύρια συνάρτηση h 1 (x)= h 1 (y) που θα δώσουν την ίδια ακολουθία ελέγχων (secondary clustering). Πώς θα μπορούσαμε να το αποφύγουμε αυτό;
Διπλός Κατακερματισμός (Double Hashing)
Βασική Ιδέα: όταν υπάρχει σύγκρουση, βρίσκουμε την επόμενη διαθέσιμη θέση στον πίνακα ελέγχοντας i h 2 (x) θέσεις πιο μπροστά, για i=0,1,2,..., όπου h 2 (x) μία δεύτερη συνάρτηση hash. Δηλαδή μία άλλη συνάρτηση hash καθορίζει την ακολουθία ελέγχων. Αν h 1 η συνάρτηση hash που δίνει τις αρχικές θέσεις στον πίνακα, τότε η συνάρτηση: h(x,i) = (h 1 (x) + i h 2 (x)) mod m, i=0,1,2,... θα καθορίζει την ακολουθία ελέγχων.
Διπλός Κατακερματισμός (Double Hashing) Χρειάζεται προσεκτική επιλογή της δεύτερης συνάρτησης hash h 2 (x): Πρέπει επίσης να είναι καλή συνάρτηση hash (να κάνει όσο το δυνατόν ομοιόμορφο κατακερματισμό). Πρέπει να υλοποιείται εύκολα. Πρέπει να διαφέρει σημαντικά από την αρχική συνάρτηση hash h 1 (x). Δηλαδή στοιχεία που απεικονίζονται στην ίδια θέση με την h 1 πρέπει να απεικονίζονται σε τελείως διαφορετικές θέσεις με την h 2. Ποτέ να μην επιστρέφει την τιμή 0 mod m. Μία καλή και τυπική επιλογή είναι: h 2 (x)= p - (x mod p) όπου p πρώτος μικρότερος του m.
Παράδειγμα h 1 (x) = x mod 7, h 2 (x) = 5 - (x mod 5), (m=7, p=5) h(x,i) = (h 1 (x) + i h 2 (x)) mod 7, i=0,1,2,... έλεγχοι: Τώρα δεν υπάρχει πρόβλημα με την εισαγωγή του 47 και του 55 (λ>1/2) insert(76) 76%7 = insert(10) 10%7 = insert(93) 93%7 = insert(40) 40%7 = insert(47) 47%7 = (47%5) = insert(55) 55%7 = (55%5) = 5
Διπλός Κατακερματισμός - Απόδοση Πλεονεκτήματα: Για κάθε λ<1 ο διπλός κατακερματισμός εγγυάται ότι θα βρει μία κενή θέση για την εισαγωγή νέου στοιχείου (αρκεί να επιλέξουμε κατάλληλο μέγεθος πίνακα και συναρτήσεις hash). Αποφεύγονται τα προβλήματα των ομαδοποιήσεων στα στοιχεία (primary clustering και secondary clustering). Το αναμενόμενο κόστος για τις βασικές πράξεις προσεγγίζει το βέλτιστο (random hash): Κόστος επιτυχούς αναζήτησης: Κόστος ανεπιτυχούς αναζήτησης: Μειονεκτήματα: Χρειάζεται ένας έξτρα υπολογισμός hash.
Μέσο πλήθος ελέγχων λλ
Μέθοδος του Brent (Brent’s Method)
Μέθοδος του Brent Είναι μία τεχνική που προσπαθεί να ελαχιστοποιήσει το μέσο κόστος επιτυχούς αναζήτησης στις μεθόδους hash ανοικτής διευθυνσιοδότησης. Χρησιμοποιεί την ηλικία ενός στοιχείου x, που αποθηκεύεται μαζί με το στοιχείο, η οποία είναι ίση με το πλήθος των ελέγχων που έγιναν μέχρι να τοποθετηθεί το στοιχείο x στον πίνακα (εκτός της αρχικής θέσης). Αν A[x 0 ], A[x 1 ], A[x 2 ], …, A[x i ] η προκαθορισμένη ακολουθία των θέσεων που ελέγχθηκαν και το x τοποθετήθηκε στην θέση A[x i ], τότε age(x)=i.
Μέθοδος του Brent Κατά την εισαγωγή ενός στοιχείου x βρίσκουμε την κατάλληλη κενή θέση A[x i ] όπως καθορίζει η ακολουθία ελέγχων και ορίζουμε την ηλικία του age(x)=i. Έστω ένα στοιχείο y που έχει ήδη τοποθετηθεί στον πίνακα στην θέση A[x j ] με ηλικία age(y)=j<i-1 από την ίδια ακολουθία ελέγχων του x. Αν η θέση A[y j+1 ] είναι κενή τότε μετακινούμε το y στην θέση αυτή (αυξάνοντας την ηλικία του κατά 1), ενώ το x το τοποθετούμε στη θέση A[x j ] (θέση που είχε το y). Έτσι η τελική ηλικία τους θα είναι: age(x)=j<i-1<i, age(y)=j+1<i. Με τον τρόπο αυτό κρατάμε τον μέσο όρο της ηλικίας των στοιχείων όσο το δυνατόν μικρό. Η ανάλυση της απόδοσης της μεθόδου αυτής είναι δύσκολη.
Κατακερματισμός Πολλαπλής Επιλογής (Multiple-Choice Hashing)
Κατακερματισμός Πολλαπλής Επιλογής Είναι μία μέθοδος κατακερματισμού με αλυσίδες (hashing with chaining) στην οποία κατά την εισαγωγή ενός στοιχείου x επιλέγεται η αποθήκευσή του σε μία αλυσίδα ανάμεσα από d 2 διαφορετικές. Η συνάρτηση hash απεικονίζει το στοιχείο x σε d θέσεις: A[x 0 ], A[x 1 ], …, A[x d-1 ]. Κατά την εισαγωγή του x ελέγχονται τα μεγέθη των αλυσίδων που βρίσκονται στις θέσεις αυτές και επιλέγεται η αλυσίδα A[x i ] με το μικρότερο μήκος (για κάποιο i {0,1,…,d-1}) τη στιγμή της εισαγωγής του x.
Κατακερματισμός Πολλαπλής Επιλογής Κατά την εισαγωγή του x ελέγχονται οι 4 λίστες που του αντιστοιχούν. Η λίστα με το μικρότερο μέγεθος είναι η 3 η. Έτσι το x εισάγεται στην 3 η αλυσίδα. Παράδειγμα με d= Κάδος 0 Κάδος 1 Κάδος 3 Κάδος m-1 … 4 x
Κατακερματισμός Πολλαπλής Επιλογής Κατά την αναζήτηση ενός στοιχείου x ελέγχονται τα στοιχεία στις d αλυσίδες: A[x 0 ], A[x 1 ], …, A[x d-1 ] παράλληλα. Δηλαδή ελέγχονται τα πρώτα τους στοιχεία, μετά τα δεύτερα κλπ, μέχρι να βρεθεί το x. Η διαγραφή ενός στοιχείου x γίνεται μετά από την αναζήτησή του στην αλυσίδα που βρίσκεται. Οι παράλληλοι έλεγχοι μπορούν να γίνουν ακόμα και ταυτόχρονα σε σύστημα με παράλληλους επεξεργαστές (parallel programming).
Κατακερματισμός Πολλαπλής Επιλογής - Απόδοση Το αναμενόμενο κόστος αναζήτησης μέσης περίπτωσης είναι Θ(d). Το κόστος αναζήτησης χειρότερης περίπτωσης είναι O(dW), όπου W είναι το μήκος της μεγαλύτερης αλυσίδας. Το αναμενόμενο μήκος E[W] είναι: Συνεπώς το αναμενόμενο κόστος αναζήτησης χειρότερης περίπτωσης, θεωρώντας το d ως μία μικρή σταθερά, είναι:
Ασύμμετρος Κατακερματισμός (Asymmetric Hashing)
Είναι μία παραλλαγή της μεθόδου κατακερματισμού πολλαπλής επιλογής (multiple-choice hashing) στην οποία: Ο πίνακας hash χωρίζεται σε d κομμάτια ίσου μεγέθους n/d (όπου n πολλαπλάσιο του d). Η επιλογή των d θέσεων: A[x 0 ], A[x 1 ], …, A[x d-1 ] για το στοιχείο x γίνεται ομοιόμορφα μέσα από τα d κομμάτια. Συνεπώς κάθε x i με i=0,1,…,d-1 εκλέγεται από το κομμάτι του πίνακα με δείκτες: {in/d,..., (i+1)n/d − 1}. Οι βασικές πράξεις εκτελούνται όπως και στον κατακερματισμό πολλαπλής επιλογής.
Ασύμμετρος Κατακερματισμός - Απόδοση Αν W είναι το μήκος της μεγαλύτερης αλυσίδας, τότε το αναμενόμενο μήκος είναι: όπου φ d είναι η γενίκευση του χρυσού λόγου. Συνεπώς το αναμενόμενο κόστος αναζήτησης χειρότερης περίπτωσης, θεωρώντας το d ως μία μικρή σταθερά, είναι πάλι: Όμως για μεγαλύτερες τιμές του d το κόστος είναι σημαντικά καλύτερο από τον κατακερματισμό πολλαπλής επιλογής.
Κατακερματισμός LCFS (LCFS Hashing)
Κατακερματισμός LCFS Είναι μία μορφή κατακερματισμού με ανοικτή διευθυνσιοδότηση στην οποία αλλάζει η στρατηγική χειρισμού των συγκρούσεων. Στην ανοικτή διευθυνσιοδότηση, όταν προκύπτει μία σύγκρουση μεταξύ δύο στοιχείων, δίνεται προτεραιότητα στο στοιχείο που μπήκε πρώτο στον πίνακα οπότε τα επόμενα προχωράνε στις επόμενες θέσεις που καθορίζει η ακολουθία ελέγχων (FCFS – first come first served). Με τον κατακερματισμό LCFS συμβαίνει ακριβώς το αντίθετο (last come first served). Δηλαδή: Κάθε νεοεισερχόμενο στοιχείο x αποθηκεύεται πάντοτε στην πρώτη θέση x 0 που καθορίζει η ακολουθία ελέγχων. Αν η θέση αυτή είναι κατειλημμένη από άλλο στοιχείο y, το οποίο τοποθετήθηκε από την ακολουθία του στη θέση y j, τότε το y προχωράει στην επόμενη θέση της ακολουθίας του, δηλαδή στην θέση y j+1. Αν και εκεί υπάρχει άλλο στοιχείο z συνεχίζεται η διαδικασία ομοίως.
Κατακερματισμός LCFS - Απόδοση Αποδεικνύεται ότι το αναμενόμενο κόστος αναζήτησης χειρότερης περίπτωσης είναι φραγμένο από το: Όπου: Συνεπώς το αναμενόμενο κόστος αναζήτησης χειρότερης περίπτωσης είναι: Θ(logn).
Robin-Hood Κατακερματισμός (Robin-Hood Hashing)
Robin-Hood Κατακερματισμός Είναι μία μορφή κατακερματισμού με ανοικτή διευθυνσιοδότηση η οποία προσπαθεί να εξισορροπήσει τους χρόνους αναζήτησης των στοιχείων χρησιμοποιώντας μία πιο δίκαιη στρατηγική χειρισμού των συγκρούσεων. Κατά την εισαγωγή ενός στοιχείου x: Έστω ότι η ακολουθία ελέγχων καθορίζει ότι το x θα τοποθετηθεί στην θέση x i. Αν η θέση αυτή είναι κατειλημμένη από άλλο στοιχείο y, το οποίο τοποθετήθηκε από την ακολουθία του στη θέση y j, τότε το νεότερο από τα δύο στοιχεία x, y θα προχωρήσει παρακάτω. Η ηλικία καθορίζεται πάλι ως εξής: age(x)=i και age(y)=j. Αν i j τότε το x προχωράει στην επόμενη διαθέσιμη θέση της ακολουθίας του, δηλαδή στην πρώτη κενή θέση της: x i+1, x i+2,… Αν i>j τότε το y προχωράει στην επόμενη διαθέσιμη θέση της ακολουθίας του, δηλαδή στην πρώτη κενή θέση της: y j+1, y j+2,…
Robin-Hood Κατακερματισμός - Απόδοση Αποδεικνύεται ότι το αναμενόμενο κόστος αναζήτησης χειρότερης περίπτωσης είναι στενά φραγμένο από: Θ(log log n). Συνεπώς είναι εξίσου ανταγωνιστικός με την μέθοδο κατακερματισμού πολλαπλής επιλογής.
Cuckoo Κατακερματισμός (Cuckoo Hashing)
Cuckoo Κατακερματισμός Είναι μία μορφή κατακερματισμού που συνδυάζει τον διπλό κατακερματισμό (double hashing) και τον τέλειο κατακερματισμό (perfect hashing). Βασική Ιδέα: Χρησιμοποιούνται δύο πίνακες hash Τ 1, Τ 2 του ίδιου μεγέθους m. Χρησιμοποιούνται δύο καθολικές συναρτήσεις hash h 1 (x), h 2 (x) μία για τον κάθε πίνακα. Κάθε στοιχείο x είναι αποθηκευμένο είτε στον έναν πίνακα στην θέση Τ 1 [h 1 (x)], είτε στον άλλο στην θέση Τ 2 [h 2 (x)]. Δεν υπάρχει άλλη επιλογή!
Cuckoo Κατακερματισμός Αναζήτηση: Για την αναζήτηση ενός στοιχείου x απλά ελέγχουμε αν βρίσκεται στο Τ 1 [h 1 (x)] ή στο Τ 2 [h 2 (x)]. Απαιτούνται μόνο δύο έλεγχοι για την εύρεση του x. Το κόστος σε κάθε περίπτωση είναι: O(1).... T1T1 T2T2 h 1 (x) h 2 (x) x a c d b t y z x Που είναι το x ?
Cuckoo Κατακερματισμός Εισαγωγή του x: Ελέγχουμε αν είναι άδειο το κελί Τ 1 [h 1 (x)] οπότε τοποθετούμε αμέσως το x. Αν υπάρχει εκεί άλλο στοιχείο y, δηλαδή ισχύει h 1 (x)]=h 1 (y), τότε τοποθετούμε το x στη θέση του y και ελέγχουμε αν είναι άδειο το κελί Τ 2 [h 2 (y)]. Αν είναι τοποθετούμε εκεί το y. Αν υπάρχει εκεί άλλο στοιχείο z, δηλαδή ισχύει h 2 (y)=h 2 (z), τότε τοποθετούμε το y στη θέση του z και ελέγχουμε αν είναι άδειο το κελί Τ 1 [h 1 (z)]. Αν είναι τοποθετούμε εκεί το z. Αν δεν είναι κενό επαναλαμβάνουμε ομοίως.
Insert(x, 1) Αλγόριθμος Insert(x, i): 1. Τοποθετούμε το x στη θέση h i (x) του T i 2. Αν το T i [h i (x)] ήταν άδειο, τότε τέλος 3. Αν το T i [h i (x)] περιείχε το y, Insert(y, 3–i) Παράδειγμα... T1T1 T2T2 h 1 (e) = h 1 (a) h 2 (y) = h 2 (a) e a c d b t y z x h 1 (y)
Cuckoo Κατακερματισμός – Απόδοση Διαγραφή: Κάνουμε αναζήτηση του x και το διαγράφουμε. Ιδιότητες: Η αναζήτηση και η διαγραφή απαιτεί το πολύ δύο προσπελάσεις ακόμα και στη χειρότερη περίπτωση Μπορεί να γίνει και παράλληλα Η εισαγωγή έχει επιμερισμένο αναμενόμενο κόστος Θ(1). Κάνει καλή χρήση της μνήμης. Δεν απαιτεί δυναμική δέσμευση μνήμης. Έχει πολλές εφαρμογές και επεκτάσεις. Mitzenmacher’s survey ESA 2009
Cuckoo Κατακερματισμός – Απόδοση Μερικά προβλήματα και λύσεις: Υπάρχει περίπτωση η εισαγωγή ενός στοιχείου να καταλήξει σε αποτυχία αν δεν μπορεί να βρεθεί κενή θέση μετά από c logn ελέγχους, όπου c μία σταθερά (συνήθως c=6). Στην περίπτωση αυτή ολόκληρη η δομή κατασκευάζεται από την αρχή εισάγοντας τα n στοιχεία με διαφορετική σειρά, ή επιλέγοντας δύο διαφορετικές καθολικές συναρτήσεις h 1,h 2. Ευτυχώς έχει αποδειχθεί ότι η πιθανότητα να συμβεί αυτό είναι O(1/n). Η εισαγωγή n στοιχείων έχει αναμενόμενο κόστος O(n), συνεπώς η μέθοδος αυτή ανταγωνίζεται τον δυναμικό τέλειο κατακερματισμό (dynamic perfect hashing).
Bloom Φίλτρα
Bloom Φίλτρα - Η βασική ιδέα Όταν έχουμε ένα σύνολο ή μία λίστα, ο χώρος είναι πολύ σημαντικός, και δεν πειράζει να έχουμε false positives αλλά πειράζει να έχουμε false negatives στην αναζήτηση στοιχείων, τότε το Bloom φίλτρο είναι μία καλή εναλλακτική. Δοθέντος ενός συνόλου S = {x 1,x 2,…,x n }, πρέπει να σχεδιαστεί μία δομή για να απαντά ταχύτατα σε ερωτήματα της μορφής «Είναι το y στο S;» Η δομή θα πρέπει να είναι: Γρήγορη (πιο γρήγορη από την αναζήτηση στο S ). Μικρή (να έχει όσο το δυνατόν λιγότερα bits χώρου). Για να πετύχουμε τα παραπάνω επιτρέπουμε μία πιθανότητα λάθους για false positives: False positives: y S αλλά αναφέρεται ότι y S False negatives: y S αλλά αναφέρεται ότι y S
Bloom Φίλτρα - Η βασική ιδέα Έτσι οι δυνατές απαντήσεις στην ερώτηση «Είναι το y στο S;» είναι Πιθανόν το y να βρίσκεται στο S (με πιθανότητα p%). Σίγουρα το y δεν βρίσκεται στο S. Χρησιμοποιείται ένας πίνακας B μεγέθους m bits. Χρησιμοποιείται ένα προκαθορισμένο πλήθος k συναρτήσεων hash h i (x), i=1,…,k, που κάνουν ομοιόμορφη κατανομή. Κάθε στοιχείο x απεικονίζεται από τις k συναρτήσεις σε k θέσεις τις οποίες ενεργοποιεί (θέτει το bit τους 1).
Bloom Φίλτρα Ξεκινάμε με τον πίνακα Β των m bit γεμάτο με 0. Απεικονίζουμε κάθε στοιχείο x j του S με τις k συναρτήσεις. Αν h i (x j )=a, τότε B[a]= B B Για να ελέγξουμε αν το y είναι στο S, ελέγχουμε το B στα h i (y). Όλες οι k τιμές θα πρέπει να είναι B B Πιθανό να έχουμε false positives: όλες οι k τιμές είναι 1, αλλά το y δεν ανήκει στο S. n στοιχεία m = cn bits k συναρτήσεις κατακερματισμού
Παράδειγμα Προσομοιωτής:
Παράδειγμα Ακόμα και ένα bit να είναι 0 στην αναζήτηση είμαστε σίγουροι ότι δεν υπάρχει το στοιχείο στην δομή
Παράδειγμα
Παρόλο που όλα τα bit είναι 1 στην αναζήτηση το στοιχείο δεν υπάρχει στην δομή (false positive)
Bloom Φίλτρα - Απόδοση Δεν επιτρέπονται οι διαγραφές στην δομή (μόνο εισαγωγές και αναζητήσεις). Επιτυγχάνεται κόστος Ο(1) σε κάθε περίπτωση για εισαγωγή και αναζήτηση, αλλά με την αναζήτηση να επιστρέφει και false positives. Η παράμετρος k (δηλαδή το πλήθος των συναρτήσεων hash που χρησιμοποιούνται) είναι σημαντική για το ποσοστό των false positives. Αποδεικνύεται ότι το βέλτιστο πλήθος συναρτήσεων το οποίο ελαχιστοποιεί την πιθανότητα των false positives είναι:
Παράδειγμα Αν m/n = 8 Βέλτιστο k = 8 ln 2 =
Στρατηγικές
Όταν ο πίνακας γεμίζει αρκετά Για τις μεθόδους κατακερματισμού με αλυσίδες μπορούμε να έχουμε αρκετά γεμάτο τον πίνακα (λ κοντά στο 1) χωρίς μεταβολές. Για τις μεθόδους ανοικτής διευθυνσιοδότησης καλό είναι να ο πίνακας hash να μην γεμίσει πάνω από το μισό. Αν συμβεί λ 1/2 τότε συστήνεται rehashing: Ο πίνακας επεκτείνεται κοντά στο διπλάσιο του μεγέθους του, όχι ακριβώς στο 2m (γιατί δεν είναι πρώτος) αλλά στον κοντινότερο πρώτο (όταν απαιτείται). Επιλέγεται μία νέα συνάρτηση κατακερματισμού (οι νέες) ή προσαρμόζονται οι παράμετροί τους. Γίνεται εισαγωγή όλων των στοιχείων στον εκτεταμένο πίνακα.
Όταν ο πίνακας γεμίζει αρκετά Η διαδικασία rehashing έχει περισσότερα πλεονεκτήματα από το να σχεδιάσουμε πιο πολύπλοκες στρατηγικές για να λύσουμε το πρόβλημα: Απαιτεί γραμμικό κόστος O(n). Τα στοιχεία απλώνονται στον διπλάσιο χώρο και η απόδοση βελτιώνεται σημαντικά (αποφεύγονται οι πολλές συγκρούσεις). Ορισμένες παράμετροι μπορούν να συντονιστούν ξανά. Δεν επιτρέπονται αποτυχίες (όπως με τον τετραγωνικό έλεγχο). Καθαρίζει ο πίνακας από όλα τα στοιχεία που σημειώθηκαν ως διαγραμμένα. Επιτρέπει την εκκίνηση με ένα μικρό μέγεθος πίνακα και το σταδιακό διπλασιασμό του όταν απαιτείται.
Μεγάλα σύνολα δεδομένων Αν έχουμε πολύ μεγάλα σύνολα δεδομένων (n>>m) που δεν μπορούν τα κλειδιά των στοιχείων τους να χωρέσουν σε πίνακα hash στην κύρια μνήμη; Μπορούμε να χρησιμοποιήσουμε πολλαπλά επίπεδα hash (όπως στον τέλειο κατακερματισμό) ώστε το 1 ο τουλάχιστον επίπεδο να χωράει στην μνήμη. Σε κάθε περίπτωση ένας (ή περισσότεροι πίνακες hash ανάλογα με την μέθοδο που θα εφαρμόσουμε) θα πρέπει να αποθηκευτούν στην δευτερεύουσα μνήμη. Έτσι χωρίζονται σε μπλοκ μεγέθους Β και δεικτοδοτούνται. Χρειάζονται καλές στρατηγικές για να μειώσουμε το I/O κόστος για τις βασικές πράξεις.
Επεκτάσιμος Κατακερματισμός (Extendible Hashing)
Επεκτάσιμος Κατακερματισμός Βασική Ιδέα: Κάθε τμήμα του πίνακα hash αποθηκεύεται σε ένα μπλοκ δίσκου (Β). Χρησιμοποιείται ένας κατάλογος δεικτών (directory) που χωράει σε ένα μπλοκ (ή περισσότερα) ο οποίος δεικτοδοτεί τα τμήματα του πίνακα. Αν γεμίσει ένα μπλοκ (υπερχείλιση) τότε θα πρέπει να κάνουμε rehashing. Αλλά το να διπλασιάσουμε το μέγεθος ολόκληρου του πίνακα (άρα και το πλήθος όλων των μπλοκ) είναι πολύ ακριβό. Στρατηγική: Αντί να διπλασιάσουμε όλη τη δομή, διπλασιάζουμε μόνο το μπλοκ που υπερχείλισε και διπλασιάζουμε τον κατάλογο (άρα και όσα μπλοκ αυτός έχει) ορίζοντας νέα αντιστοιχία. Ο κατάλογος είναι πολύ μικρότερος από το πλήθος όλων των μπλοκ και άρα ο διπλασιασμός του είναι πολύ φθηνότερος. Το κόλπο είναι το πώς θα ορίσουμε σωστά τη νέα αντιστοιχία.
Παράδειγμα Ο κατάλογος περιέχει ως στοιχεία τα k πρώτα bits των κλειδιών. Όλα τα κλειδιά με το ίδιο πρόθεμα (k-bits) δεικτοδοτούνται στο ίδιο μπλοκ. Αν χωράνε στο ίδιο μπλοκ δύο ή περισσότερες ομάδες κλειδιών, τότε ο κατάλογος δεικτοδοτεί τις ομάδες με κοινό πρόθεμα j k bits (2) (2) (3) (3) (2) directory (k=3) j
Επεκτάσιμος Κατακερματισμός Η εισαγωγή νέου στοιχείου γίνεται ως εξής: Βρίσκουμε το μπλοκ b στο οποίο ανήκει. Αν υπάρχει χώρος το τοποθετούμε εκεί. Διαφορετικά, αν το μπλοκ είναι γεμάτο, κάνουμε διάσπαση (split): Αυξάνουμε το j του μπλοκ b κατά 1. Δημιουργούμε καινούργιο μπλοκ b’ με το νέο j. Μοιράζουμε τα κλειδιά ανάλογα με το πρόθεμά τους. Προσθέτουμε τον δείκτη του b’ στον κατάλογο.
Εισαγωγή χωρίς διάσπαση (2) (2) (3) (3) (2) insert(11011) (2) (2) (3) (3) (2) Χρειάζονται μόνο 2 Ι/Ος
Εισαγωγή με διάσπαση (2) (2) (3) (3) (2) insert(11000) (2) (2) (3) (3) (3) (3) b b’ b Χρειάζονται μόνο 3 Ι/Ος
Εισαγωγή με επέκταση καταλόγου Η εισαγωγή του δεν μπορεί να γίνει καθώς δεν υπάρχει χώρος και ούτε μπορεί να γίνει διάσπαση μπλοκ. Η λύση; Επέκταση του καταλόγου προσθέτοντας ένα επιπλέον bit 0 ή 1 σε κάθε στοιχείο του και ενημερώνοντας την νέα αντιστοιχία με τα υπάρχοντα μπλοκ (2) (2) (2) insert(10010)
Επεκτάσιμος Κατακερματισμός Η διαγραφή στοιχείων γίνεται ακριβώς αντίστροφα με πιθανές συγχωνεύσεις. Αν έχουμε μαζικές εισαγωγές και διαγραφές τότε μπορεί να συμφέρει να κάνουμε rehashing για να πάρουμε την τελική δομή. Αν τα δεδομένα είναι τεράστια σε πλήθος και ο κατάλογος μεγαλώσει σημαντικά τότε έχουμε δύο επιλογές: Να μεγαλώσουμε τα τμήματα με τα στοιχεία να μην έχουν μέγεθος μόνο ένα μπλοκ Β αλλά 2Β, 3Β κλπ. Να οργανώσουμε τον κατάλογο σε Β-Δέντρο!
ΤΕΛΟΣ