Οικονοµικό Πανεπιστήµιο Αθηνών Τµήµα Πληροφορικής Φθινοπωρινό Εξάµηνο 2015 Δοµές Δεδοµένων - Εργασία 2 Διδάσκων: E. Μαρκάκης Ταξινόµηση και Ουρές Προτεραιότητας Σκοπός της 2 ης εργασίας είναι η εξοικείωση µε τους αλγορίθµους ταξινόµησης και µε τις ουρές προτεραιότητας, µία από τις πιο βασικές και χρήσιµες δοµές δεδοµένων στη σχεδίαση αλγορίθµων. Διαβάστε προσεκτικά την εκφώνηση και τα ζητούµενα της εργασίας. Η εργασία αφορά ερωτήµατα που συναντώνται σε απλά recommendation systems, όπως π.χ. συστήµατα που προτείνουν ταινίες σε θεατές. Έστω ότι θέλουµε να προτείνουµε τις k καλύτερες ταινίες για κάποια παράµετρο k, µε κριτήριο τα likes που έχουν πάρει σε κάποιο κοινωνικό δίκτυο ή site. Για την υλοποίηση θα χρησιµοποιήσετε τον εξής ΑΤΔ: ΑΤΔ Movie: Είναι ο ΑΤΔ που αναπαριστά µια ταινία και θα είναι στο αρχείο Movie.java. Τα αντικείµενα αυτού του τύπου θα πρέπει να περιέχουν: ένα πεδίο τύπου int µε όνοµα id. Ο αριθµός αυτός είναι µοναδικός για κάθε ταινία. ένα πεδίο τύπου String µε όνοµα title, για το όνοµα της ταινίας, ένα πεδίο τύπου int µε όνοµα likes. Είναι τα likes που παίρνει κάθε ταινία, µπορείτε να προσθέσετε και άλλα πεδία στην κλάση αυτή. Για να συγκρίνετε ταινίες µεταξύ τους, θα χρησιµοποιήσετε το πεδίο likes. Σε περίπτωση που το πεδίο likes είναι ίδιο σε 2 ή παραπάνω ταινίες, θα κάνετε σύγκριση αλφαβητικά µε βάση τα ονόµατα των ταινιών (δηλαδή προηγείται η ταινία µε λεξικογραφικά µικρότερο όνοµα). Εποµένως, η κλάση Movie θα πρέπει να υλοποιεί το interface Comparable<Movie> (και συνεπώς της συνάρτησης compareto), έτσι ώστε να µπορέσετε να την χρησιµοποιήσετε για σύγκριση ή ταξινόµηση. Δεδοµένα εισόδου: Στα µέρη Α και Β παρακάτω θα χρειαστεί να διαβάσετε ένα αρχείο το οποίο θα είναι στην παρακάτω µορφή: 1 Youth 45 13 The Gangs of New York 55 22 Interstellar 34 12 Clockwork Orange 45 9 La Dolce Vita 64
To παραπάνω format έχει την εξής ερµηνεία: το πρώτο πεδίο είναι το id κάθε ταινίας, στη συνέχεια ακολουθεί ο τίτλος της ταινίας (δεν υπάρχει προκαθορισµένος αριθµός λέξεων) και το τελευταίο πεδίο είναι τα likes που έχει πάρει. Στο παράδειγµα, η ταινία Interstellar έχει id ίσο µε 22 και έχει 34 likes. Μέρος A. Υλοποιήστε ένα πρόγραµµα το οποίο θα διαβάζει ένα αρχείο στην παραπάνω µορφή και θα τυπώνει τις k καλύτερες ταινίες. Η παράµετρος k θα είναι µια σταθερά ορισµένη στην αρχή του προγράµµατος σας, την οποία θα µπορείτε να αλλάζετε για να κάνετε διαφορετικά τρεξίµατα. Για το Μέρος Α, θα στηριχτείτε σε µια απλοϊκή υλοποίηση, η οποία θα πρέπει να γίνει χρησιµοποιώντας κάποιον αλγόριθµο ταξινόµησης για όλες τις ταινίες του αρχείου. Μπορείτε να υλοποιήσετε οποιαδήποτε από τις µεθόδους Mergesort, Quicksort ή Heapsort. Είστε ελεύθεροι να διαλέξετε όποια µέθοδο θέλετε. Απαγορεύεται όµως να χρησιµοποιήσετε έτοιµες µεθόδους ταξινόµησης που παρέχονται από την Java. Θα πρέπει να υλοποιήσετε τη δική σας µέθοδο. H υλοποίησή σας µπορεί να είναι είτε µε πίνακες είτε µε λίστες. Με k=3, στο παράδειγµα που φαίνεται παραπάνω η εκτύπωση του προγράµµατος θα πρέπει να είναι στη µορφή: The top k movies are: La Dolce Vita The Gangs of New York Clockwork Orange Υπόψιν ότι επιλέχθηκε ως 3 η ταινία το Clockwork Orange καθώς ενώ έχει το ίδιο σκορ µε την 4 η ταινία προηγείται αλφαβητικά. Μέρος Β. Ένα µειονέκτηµα της παραπάνω προτεινόµενης υλοποίησης είναι ότι πρέπει να αποθηκεύσετε πρώτα όλες τις ταινίες για να αποφασίσετε ποιες είναι οι k καλύτερες. Και εφόσον στηρίζεστε σε αλγόριθµο ταξινόµησης, θα έχετε πολυπλοκότητα τουλάχιστον O(NlogN) αν έχετε N ταινίες. Όταν όµως η παράµετρος k είναι πολύ µικρή σε σχέση µε το Ν, τότε αξίζει να δούµε πώς θα µπορούσαµε να έχουµε µια καλύτερη υλοποίηση. Στο µέρος Β, ζητείται να κάνετε µια διαφορετική υλοποίηση στην οποία θα χρησιµοποιήσετε µια ουρά προτεραιότητας χωρητικότητας το πολύ k στοιχείων. Δεν επιτρέπεται να χρησιµοποιήσετε δοµή που να αποθηκεύει όλες τις ταινίες του αρχείου εισόδου. Επιτρέπεται η αποθήκευση ταινιών µόνο σε µια ουρά προτεραιότητας η οποία θα έχει µέγιστο µέγεθος k. Σκεφτείτε πώς αρκεί µια τέτοια ουρά για να λύσετε το πρόβληµα της επιλογής των k καλύτερων ταινιών. ΑΤΔ ουράς προτεραιότητας. Θα χρειαστείτε µια ουρά προτεραιότητας, όπου θα αποθηκεύετε αντικείµενα τύπου Movie. O ΑΤΔ της ουράς θα πρέπει να είναι στο αρχείο PQ.java. Η υλοποίηση της ουράς θα γίνει µε χρήση σωρού, δηλαδή χρησιµοποιώντας έναν πίνακα (ονοµάστε τον pq) όπου η αποθήκευση αντικειµένων ξεκινά από τη θέση 1. Μπορείτε να βασιστείτε στην ουρά προτεραιότητας του Εργαστηρίου 5 ή σε αυτή του βιβλίου και των διαφανειών, και να τις τροποποιήσετε κατάλληλα, ή µπορείτε να φτιάξετε εξ ολοκλήρου τη δική σας ουρά. Η ουρά σας θα πρέπει να διαθέτει, εκτός από τις λειτουργίες insert και getmax, που είδαµε και στο µάθηµα, και κάποιες επιπλέον µεθόδους. Συγκεκριµένα, οι µέθοδοι που απαιτούνται είναι οι εξής (µπορείτε να έχετε κι άλλες µεθόδους µέσα στην υλοποίησή σας): boolean isempty(): έλεγχος για το αν είναι άδεια η ουρά. int size(): επιστρέφει τον αριθµό ενεργών στοιχείων στην ουρά. void insert(movie x): εισαγωγή αντικειµένου στην ουρά. Η εισαγωγή θα πρέπει να καλεί και µια µέθοδο resize που θα κάνει διπλασιασµό του πίνακα της ουράς όταν η ουρά έχει γεµίσει κατά το 75%.
Movie max(): επιστρέφει το στοιχείο µε τη µέγιστη προτεραιότητα χωρίς να το αφαιρεί από την ουρά. Movie getmax(): Αφαιρεί και επιστρέφει το αντικείµενο µε τη µέγιστη προτεραιότητα. Η πολυπλοκότητα που πρέπει να έχει η κάθε µέθοδος, σε µια ουρά µε n αντικείµενα δίνεται στον παρακάτω πίνακα. Μέθοδος size, isempty insert max getmax Πολυπλοκότητα Ο(1) O(logn) (*δειτε σχόλιο παρακάτω*) O(1) O(logn) Σχόλιο: Για την insert, η απαίτηση για Ο(logn) είναι µόνο όταν δεν χρειάζεται να καλέσει τη resize για την επέκταση του πίνακα. Hint: Οι σκέψεις σας για το τρόπο επίλυσης του προβλήµατος δεν πρέπει να σας οδηγήσουν σε εσωτερικές αλλαγές στον τρόπο λειτουργίας του σωρού αλλά σε ορθή χρήση της δοµής. Σκεφτείτε προσεκτικά πως ορίζεται η προτεραιότητα µιας ταινίας για τις ανάγκες του συγκεκριµένου προβλήµατος και κατασκευάσετε το κατάλληλο Comparator. Οδηγίες υλοποίησης για τα µέρη Α και Β: Το πρόγραµµα σας για το µέρος Α πρέπει να λέγεται Top_k.java. H υλοποίηση της ουράς προτεραιότητας θα πρέπει να είναι στο αρχείο PQ.java. Το πρόγραµµα για το µέρος Β πρέπει να λέγεται Top_k_withPQ.java. Αν θέλετε, µπορείτε να υλοποιήσετε την ουρά προτεραιότητας µε generics µε bonus 10% στον βαθµό της εργασίας. Σχολιάστε στο pdf αρχειο που θα παραδώσετε µε την εργασία την πολυπλοκότητα των 2 προγραµµάτων και το αν είναι τελικά συµφέρον να χρησιµοποιήσουµε το 2 ο πρόγραµµα σε περιπτώσεις όπου το k είναι αρκετά µικρότερο του συνολικού αριθµού ταινιών. Για το διάβασµα του αρχείου εισόδου, µπορείτε να χρησιµοποιήσετε έτοιµες µεθόδους της Java. Μπορείτε επίσης να χρησιµοποιήσετε έτοιµες µεθόδους για να επεξεργαστείτε µεταβλητές τύπου String. Επιπλέον, για το χειρισµό των δεδοµένων εισόδου µπορείτε (και προτείνεται) να χρησιµποιήσετε τις δοµές που έχουµε δει µέχρι στιγµής στο µάθηµα, τα εργαστήρια ή την 1 η εργασία σας: συνδεδεµένη λίστα (µονή ή διπλή), ουρές και στοιβες µε χρήση λίστας ή πίνακα. Μπορείτε να χρησιµοποιήσετε δικές σας υλοποιήσεις ή τις έτοιµες υλοποιήσεις του εργαστήριου αλλά όχι τις έτοιµες της Java. Δεν χρειάζεται να ασχοληθείτε µε ανίχνευση λαθών στο format του αρχείου εισόδου. Το αρχείο εισόδου θα δίνεται ως όρισµα κατά την εκτέλεση του πρόγραµµατος από τη γραµµή εντολών, για παράδειγµα η εκτέλεση της main του ερωτήµατος Α θα γίνεται ως εξής: java Top_k path_to_file και αντίστοιχα για το ερώτηµα Β. Γ. Έστω ότι διαβάζουµε και πάλι ένα αρχείο εισόδου µε το ίδιο format, και µας ενδιαφέρει να απαντάµε σε ερωτήσεις της µορφής: «Πόσα likes χρειάζεται να πάρει µια ταινία για να είναι στο top 50% των ταινιών που εξετάζουµε;». Αυτο σηµαίνει ότι πρέπει να υπολογίζουµε το median από τα likes που βλέπουµε. O median µιας ακολουθίας ακεραίων είναι ο αριθµός που θα βρίσκεται στη µέση της ακολουθίας αν την ταξινοµήσουµε. Π.χ. στην ακολουθία 13, 17, 11, 24, 19, 8, 14, ο median είναι ο 14. Αν η ακολουθία έχει άρτιο αριθµό ακεραίων, τότε θα κάνουµε τη
σύµβαση να παίρνουµε ως median τον µικρότερο αριθµό από το µεγαλύτερο µισό, δηλαδή στην ακολουθία 13, 17, 11, 24, 19, 8, θα θεωρήσουµε ως median τον 17. Θέλουµε να υλοποιήσουµε µια δοµή, η οποία να µπορεί να διατηρεί ενηµερωµένο τον median δυναµικά, όπως επεξεργάζεται κάθε νέα ταινία από το αρχείο εισόδου. Θέλουµε δηλαδή ένα πρόγραµµα, το οποίο µετά την ανάγνωση κάθε γραµµής του αρχείου εισόδου, να µπορεί εύκολα να υπολογίσει τον median αριθµό από likes, µε βάση τις ταινίες που έχουµε επεξεργαστεί µέχρι εκείνη τη χρονική στιγµή (γενικεύοντας, η δυναµική ενηµέρωση του median σε ενα stream από δεδοµένα είναι σηµαντικό πρόβληµα σε αρκετές άλλες εφαρµογές). Υπάρχουν φυσικά πολλοί τρόποι για να λυθεί ένα τέτοιο πρόβληµα. Σκοπός του µέρους Γ είναι η αποδοτική επίλυση του προβλήµατος µε τη χρήση 2 ουρών προτεραιότητας. Θα πρέπει λοιπόν να χρησιµοποιήσετε κατάλληλα 2 ουρές προτεραιότητας έτσι ώστε η επεξεργασία που θα κάνετε µετά την ανάγνωση κάθε γραµµής του αρχείου να επιτρέπει την εύκολη ενηµέρωση του median. Οδηγίες υλοποίησης: Το πρόγραµµα σας πρέπει να λέγεται Dynamic_Median.java. Για να µην είναι πολύ µεγάλη η εκτύπωση της εξόδου, απαιτείται να τυπώνετε τον median µόνο µετά από κάθε 5 γραµµές του αρχείου εισόδου. Το πρόγραµµά σας θα πρέπει µε την ανάγνωση κάθε γραµµής εισόδου να κάνει κάποια επεξεργασία, αλλά ο υπολογισµός και η εκτύπωση του median θα γίνεται µόνο όταν (i%5 == 0). Π.χ. στο παράδειγµα που υπάρχει στο Μέρος Α, θα τυπωνόταν µόνο 1 φορά το εξής: Median = 45 likes, achieved by Movie: Clockwork Orange Για απλότητα, δεν θα χρησιµοποιήσουµε αρχεία εισόδου άνω των 500 γραµµών. Σχολιάστε στο pdf αρχειο που θα παραδώσετε µε την εργασία την πολυπλοκότητα του προγράµµατος. Π.χ. όταν διαβάζετε µια γραµµή του αρχείου εισόδου, τι πολυπλοκότητα έχει η εισαγωγή της ταινίας στη δοµή σας; Επίσης, σε κάθε γραµµή i, µε (i%5 ==0), όπου πρέπει να τυπώσουµε τον median, τι πολυπλοκότητα έχει ο υπολογισµός του median; Οδηγίες Παράδοσης Η εργασία σας θα πρέπει να µην έχει συντακτικά λάθη και να µπορεί να µεταγλωττίζεται. Εργασίες που δεν µεταγλωττίζονται χάνουν το 50% της συνολικής αξίας. Η εργασία θα αποτελείται από: 1. Τον πηγαίο κώδικα (source code). Τοποθετήστε σε ένα φάκελο µε όνοµα src τα αρχεία java που έχετε φτιάξει. Ενδεικτικά θα πρέπει να περιέχει (χρησιµοποιήστε τα όνοµατα των κλάσεων όπως ακριβώς δίνονται στην εκφώνηση): τα αρχεία Movie.java, Top_k.java, Top_k_withPQ.java και PQ.java. Επιπλέον, φροντίστε να συµπεριλάβετε όποια άλλα αρχεία πηγαίου κώδικα απαιτούνται για να µεταγλωττίζεται η εργασία σας. Φροντίστε επίσης να προσθέσετε επεξηγηµατικά σχόλια όπου κρίνετε απαραίτητο στον κώδικά σας. 2. Γράψτε µία σύντοµη αναφορά σε pdf αρχείο (οχι Word ή txt!) µε όνοµα project2- report.pdf, στην οποία θα αναφερθείτε στα εξής: a. Εξηγήστε συνοπτικά πώς υλοποιήσατε το µέρος Α (άνω όριο µισή σελίδα). b. Για το µέρος Β, εξηγήστε πώς χρησιµοποιήσατε την ουρά προτεραιότητας για να φτιάξετε το πρόγραµµα που ζητείται (άνω όριο 2 σελίδες).
c. Οµοίως για το µέρος Γ, πώς χρησιµοποιήσατε τις 2 ουρές για την δυναµική ενηµέρωση του median (άνω όριο 2 σελίδες). Όλα τα παραπάνω αρχεία θα πρέπει να µπουν σε ένα αρχείο zip. Το όνοµα που θα δώσετε στο αρχείο αυτό θα είναι ο αριθµός µητρώου σας πχ. 3030056_3030066.zip ή 3030056.zip (αν δεν είστε σε οµάδα). Στη συνέχεια, θα υποβάλλετε το zip αρχείο σας στην περιοχή του µαθήµατος «Εργασίες» στο e-class. Υποβάλετε ένα αρχείο ανα εργασία. Δεν χρειάζεται υποβολή και από τους 2 φοιτητές. Η προθεσµία παράδοσης της εργασίας είναι Παρασκευή, 18 Δεκεµβρίου 2015 και ώρα 23:59.