Δομές Δεδομένων Εργαστηριακή Άσκηση 2012-2013 Γκόγκος Νίκος Α.Μ.: 4973 Έτος: 3 ο Email: gkogkos@ceid.upatras.gr Εισαγωγικά: Η υλοποίηση του project έχει γίνει σε python [2.7]. Τα python modules είναι αυτόνομα και μπορούν να τρέξουν απλά σε έναν 2.7 python interpreter. Στη αρχή εκτέλεσης του προγράμματος ο χρήστης καλείται να διαλέξει mode, δηλαδή σε ποια δομή θα στηριχτούν οι πράξεις που θα κάνει. Έχει τη δυνατότητα να φορτώσει την βάση με τα βιβλία σε λίστα στη RAM, σε Binary Tree, σε TRIE και σε AVL tree. Όλες οι βασικές πράξεις είναι διαθέσιμες σε κάθε δομή (αναζήτηση, εισαγωγή, διαγραφή) και μπορούν να επιλεχθούν μέσω των menus. Η αναζήτηση γίνεται σε κάθε δομή αντίστοιχα με το κλειδί που έχει ζητηθεί στην εκφώνηση της άσκησης. Η δομή Books που αναφέρεται στην εκφώνηση έχει υλοποιηθεί με μία λίστα. Η python έχει την built-in function len(), η οποία επιστρέφει το μέγεθος μιας δομής (η λίστα με τα βιβλία) σε O(1). Επομένως, δεν χρειάζεται να κρατάμε μετρητή για τα βιβλία. Επιπρόσθετα, έχει δημιουργηθεί ένα HTML API Documentation χρησιμοποιώντας το sphinxmodule της python. Μπορείτε να το βρείτε στα αρχεία του project ή σε αυτό το link: Part A Για την υλοποίηση αυτού του κομματιού, αποθηκεύουμε τη συλλογή βιβλίων σε μία δυναμική λίστα. Όλες οι πράξεις που μπορεί να κάνει ο user γίνονται σε γραμμικό χρόνο. Όπου χρειάζεται προσπελαύνουμε τη λίστα σειριακά (for loop) για να έχουμε γραμμικό χρόνο. Η python προσφέρει built-in functions για τις λίστες, οι οποίες όμως τρέχουν σε O(1). Με άλλα λόγια, η λίστα χρησιμοποιείται ως ένα array. Part B Κατασκευάζουμε ένα object Binary tree, το οποίο είναι κομβοπροσανατολισμένο. Το Binary Interpolation Search χρησιμοποιεί μία λίστα με ταξινομημένα book IDs με δείκτες στο αντίστοιχο βιβλίο αντικείμενο. Ο αλγόριθμος δεν θα μπορούσε να εφαρμοστεί πάνω στην λίστα books, αφού δεν είναι sorted. Κάθε φορά που γίνεται μια αλλαγή στη συλλογή (insert, delete) πρέπει να επαναταξινομούμε αυτή τη λίστα.
Part C Σε αυτή την περίπτωση πρέπει να δημιουργούνται 2 TRIEs. Ένα βασισμένο στο title των βιβλίων και ένα βασισμένο στο lastname των συγγραφέων. Η δομή TRIE αποτελεί ένα συμπαγές ψηφιακό δέντρο. Εφόσον, τα TRIEs δεν είναι χτισμένα πάνω σε unique key (i.e ISBN), υπάρχει περίπτωση όπου θα έχουμε πολλαπλά matches για μια αναζήτηση. Επομένως, επιστρέφεται μία λίστα που περιέχει όλα τα matching books. Part D Δημιουργούμε ένα κομβοπροσανατολισμένο AVL δέντρο το οποίο είναι οργανωμένο με βάση τα ids των βιβλίων (ISBN). Σε κάθε πράξη ελέγχεται η ζύγιση (balance score) του υποδέντρου και αν έχει χαλάσει γίνονται κατάλληλες επαναζυγιστικές κινήσεις. Αυτό ελέγχεται κοιτώντας το balance score του Κρίσιμου Κόμβου Ένθεσης [ΚΚΕ]. Part E Αρχικά, parsάρουμε το Books.csv αρχείο και δημιουργούμε ένα δικό μας books database file με το εξής απλό format. Αυτό το κάνει το createdb module. Κάθε γραμμή αποτελεί ένα record-book και τα στοιχεία αυτού (ISBN, title etc) χωρίζονται με έναν delimiter= $_$. Αφού δημιουργηθεί μια βάση με πολλά βιβλία μεγέθους περίπου 100 MB, τρέχουμε test benches για κάθε δομή. Αυτό το αναλαμβάνει το PerformanceTest module. Για την ακριβή και αντικειμενική μέτρηση των χρόνων έχει χρησιμοποιηθεί το timeit module. Όσο αφορά τη λογική που ακολουθήθηκε ώστε να παρθούν μετρήσεις, έγινε μεγάλος αριθμός αναζητήσεων randomly για κάθε δομή. Σταδιακά αυξάνουμε το πλήθος της συλλογής και όσο πιο πολλά βιβλία περιέχει, τόσο πιο πολλές αναζητήσεις γίνονται. Αυτό βοηθά να γίνουν πιο αντικειμενικές οι μετρήσεις. Πιο συγκεκριμένα, randomly κάθε φορά επιλεγόταν ένα κλειδί μέσα από το σύνολο βιβλίων και επαναλαμβανόταν η αναζήτηση αυτού για ένα μικρό πεπερασμένο αριθμό. (repeatnum = 5). Έπειτα, υπολογίζουμε τον μέσο όρο αυτού. Ο λόγος που παίρνουμε την ίδια μέτρηση 5 φορές για κάθε αναζήτηση είναι προφανής. Είναι θεμιτό να έχουμε αντικειμενικές μετρήσεις, το οποίο σημαίνει ότι θα πρέπει να εξαλείψουμε τυχόν ανωμαλίες του συστήματος (π.χ κάποιο source unavailability την στιγμή που γίνεται η αναζήτηση, το οποίο θα επιφέρει απρόσμενο delay). Επομένως, βρίσκοντας το μέσο όρο εξασφαλίζουμε καλύτερη τυπική απόκλιση των μετρήσεών και άρα τα αποτελέσματα είναι πιο έγκυρα. Επίσης, σε κάθε πείραμα (για κάθε δομή) χρησιμοποιήθηκε άλλος αριθμός αναζητήσεων. Αυτό έγινε διότι έπρεπε να έχουμε αντικειμενικό δείγμα για την κάθε δομή, αλλά το όλο randomness δεν επιφέρει τα αναμενόμενα αποτελέσματα πάντα. Οπότε, σε κάποιες περιπτώσεις χρειαζόταν περισσότερες μετρήσεις. Παρακάτω υπάρχουν οι γραφικές παραστάσεις για των παραπάνω πειραμάτων. Τα graphs παράχθηκαν με matlab. Για κάθε περίπτωση έχουμε 2 ίδιες γραφικές ως προς την πληροφορία που αναπαριστούν. Απλά στην μία τα σημεία είναι ενωμένα, ενώ στην άλλη όχι. Τέλος, οι γραφικές παραστάσεις έχουν δημιουργηθεί συναρτήσει του χρόνου και του μεγέθους της συλλογής S (books size) κάθε φορά. Εδώ εξαίρεση αποτελεί η δομή TRIE, για το
οποίο ζωγραφίζουμε την γραφική παράστασή της συναρτήσει του χρόνου και του μήκους των λέξεων που ψάχνουμε. Σε όλες τις μετρήσεις, οι τυπικές αποκλίσεις των ίδιων πειραμάτων είναι μικρές. Συνεπώς, τα συμπεράσματα μπορούν να θεωρηθούν αντικειμενικά και έγκυρα. Linear search graphs: Όσο αφορά την απόδοση της γραμμικής αναζήτησης, μπορούμε να πούμε ότι τα αποτελέσματα είναι τα αναμενόμενα. Με άλλα λόγια, η αναζήτηση ολοκληρώνεται σε γραμμικό χρόνο, δηλαδή έχει complexity O(n) όπου n είναι το μέγεθος της συλλογής. Αυτό φαίνεται και στις παρακάτω γραφικές παραστάσεις. Όπου υπάρχουν μικρά spikes ίσως οφείλονται στο ότι η random κατανομή δεν είναι τόσο κοντά στο uniform distribution ή κάποιο instability του συστήματος που πάρθηκαν οι μετρήσεις.
Binary search graphs: Παρατηρούμε σύμφωνα με τις γραφικές παραστάσεις ότι η απόδοση της δυαδικής αναζήτησης προσεγγίζει αυτή που ξέρουμε από τη θεωρία. Η θεωρητική απόδοση ενός binary search είναι O(logn) σε μέση περίπτωση αλλά μπορεί να γίνει και O(n) στην χειρότερη. Αυτό οφείλεται στο ότι το Binary Tree δεν αποτελεί ισοζυγισμένο δέντρο ως προς κάποιο κριτήριο. Συνεπώς, υπάρχει η ακραία περίπτωση να παίρνει μορφή λίστας αν κάθε καινούριος κόμβος που εισάγεται γίνεται παιδί αριστερό/δεξί παιδί του προηγούμενου. Εμείς μετράμε την μέση περίπτωση και παρατηρούμε ότι η αναζήτηση τείνει να γίνει λογαριθμική. Επομένως, συμβαδίζει με τη θεωρητική απόδοση.
Binary Interpolation search graphs: Παρατηρούμε σύμφωνα με τις γραφικές παραστάσεις ότι η απόδοση του Binary Interpolation Search είναι αρκετά κοντά στο complexity που μας δίνει η θεωρία, το O(loglogn). Φυσικά, τα αποτελέσματα δεν είναι τέλεια, μιας και οι συνθήκες που παίρνουμε τις μετρήσεις δεν είναι ιδανικές (random searches, system instabilities).
TRIE search graphs: Όσο αφορά την αναζήτηση σε αυτή τη δομή, η θεωρία μας λέει ότι η πολυπλοκότητα εξαρτάται από το λ, όπου λ είναι το πλήθος χαρακτήρων του κλειδιού αναζήτησης πάνω στο TRIE. Εφόσον, το TRIE είναι συμπαγές (εκτός του ότι πιάνει πολύ λιγότερο χώρο στη RAM) αναμένουμε κάποιες αναζητήσεις να γίνονται πιο γρήγορα από ότι περιμένουμε. Αυτό οφείλεται στο ότι κάποια κλειδιά αναζήτησης μπορεί να είναι μοναδικά στο μεγαλύτερο μήκος τους αν δούμε το key ως ένα πίνακα αλφαριθμητικών. Για παράδειγμα, αν θα είχαμε κάποιο όνομα συγγραφέα που ξεκινά από ω ή άλλα ασυνήθιστα prefixes, το πιο πιθανό θα ήταν να αποθηκευτεί σε ύψος = 1 ή τουλάχιστον χαμηλό ύψος η πληροφορία, μιας και το TRIE είναι συμπαγές. Παρατηρούμε ότι για μεγάλο μήκος κλειδιών κάποιες φορές έχουμε καλύτερη απόδοση στην αναζήτηση. Αυτό οφείλεται στο ότι όσο μεγαλώνει το μήκος του κλειδιού αναζήτησης, τόσο πιο δύσκολο είναι να έχουμε κοινά κομμάτια στο string key. Για παράδειγμα, τα lastnames 15 ψηφίων θα είναι λίγα και από αυτά τα λίγα είναι απίθανο να έχουμε lastnames με ίδια prefixes μεγάλου μήκους. Γενικά, το TRIE έχει καλή απόδοση, η οποία εξαρτάται κάθε φορά από τις πληροφορίες των βιβλίων της συλλογής (πχ πολλοί με common lastname prefixes συγγραφείς, βιβλία με σχεδόν ίδιους τίτλους)
AVL search graphs: Σύμφωνα με το θεωρητικό complexity του AVL search αναμένουμε χρόνους O(logn). Αυτό επαληθεύεται στις παρακάτω γραφικές παραστάσεις.
Conclusion: Όσο αφορά τις μετρήσεις, μπορούμε να πούμε ότι χωρίς βλάβη της γενικότητας και με λίγη ελαστικότητα επαληθεύονται όλοι οι θεωρητικοί χρόνοι. Η πιο γρήγορα αναζήτηση φαίνεται να είναι αυτή του Binary Interpolation Search. Αυτό είναι αναμενόμενο αφού το time complexity του είναι O(loglogn). References: Binary Search Tree: http://code.activestate.com/recipes/577540-python-binarysearch-tree/ AVL Tree: https://github.com/pgrafov/python-avl-tree/blob/master/pyavltree.py TRIE: http://en.wikipedia.org/wiki/trie