ΣΧΟΛΙΑ 1 ΗΣ ΕΡΓΑΣΙΑΣ Σχεδίαση Αλγορίθμων 15 Μαΐου 2013
Ζητούμενα Υλοποίηση Ταξινόμησης με Συγχώνευση (Mergesort) Αναδρομική (Recursive) εκδοχή Επαναληπτική (Iterative) εκδοχή Καταμέτρηση αντιστροφών Για πίνακα [Α 1,Α 2,,Α Ν ] υπάρχει αντιστροφή όταν Α i > A j και i < j Χρήση προτύπου (template) Java 1.6
Μεθοδολογία Εξέτασης Εκτέλεση πειραμάτων με τρία σύνολα δεδομένων Σωστή έξοδος ταξινόμησης και πλήθους αντιστροφών 1000 στοιχεία 1024 στοιχεία 1048576 στοιχεία Έλεγχος πολυπλοκότητας O(n) η merge() O(nlogn) ολόκληρη λ η mergesort Επαρκής σχολιασμός
Γενικές Παρατηρήσεις Μεγαλύτερο ποσοστό επιτυχίας στην αναδρομική mergesort Σωστά αποτελέσματα επαναληπτικής mergesort χωρίς σωστό πλήθος αντιστροφών Σωστή πολυπλοκότητα στις περισσότερες εργασίες
Συνηθέστερα Λάθη Στατική ήδέσμευση χώρου μνήμης Υπόθεση ηγια αρχεία εισόδου με πλήθος στοιχείων ίσο με δύναμη του 2 Υπόθεση για άρτιο πλήθος στοιχείων «Κρέμασμα» προγράμματος στο 3 ο αρχείο εισόδου Ανάθεση ονόματος στο αρχείου εισόδου Θα έπρεπε να γίνεται μέσω ορίσματος στο command line
Αναδρομική Mergesort (1) Δά Διάσπαση αρχικού πίνακα A σε δύο βοηθητικούς B και C Εισαγωγή των στοιχείων του Α στους Β και C Κλήση αναδρομικής Mergesort στον B Κλήση αναδρομικής Mergesort στον C Συγχώνευση των (ταξινομημένων) B και C Το πλήθος αντιστροφών είναι ίσο με το πλήθος των αντιστροφών στον Β συν το πλήθος των αντιστροφών στον C συν το πλήθος των αντιστροφών που προκύπτουν κατά τη συγχώνευσή τους
Αναδρομική Mergesort (2) static long recursivemergesortandcount(integer[] A) long inversions = 0; if (A.length > 1) //Δημιουργία 2 βοηθητικών υποπινάκων από τον πίνακα A int mid = A.length/2; Integer [] B = new Integer[mid]; Integer [] C = new Integer[A.length mid]; //Εισαγωγή των στοιχείων του πίνακα A //στους ς υποπίνακες B και C αντίστοιχα for (int i = 0; i< mid; i++) B[i] = A[i]; for (int i = mid; i < Alength; A.length; i++) C[i mid] = A[i]; //Μέτρηση αντιστροφών στον αριστερό υποπίνακα B inversions += recursivemergesortandcount(b); //Μέτρηση αντιστροφών στο δεξιό υποπίνακα C inversions += recursivemergesortandcount(c); //Μέτρηση αντιστροφών κατά τη συγχώνευση των 2 //υποπινάκων σε 1 πίνακα, τον A inversions += merge(b, C, A); return inversions; //Περίπτωση πίνακα 1 στοιχείου return 0;
Επαναληπτική Mergesort (1) Μια εξωτερική επανάληψη διατρέχει τον πίνακα ανά blocks (με μέγεθος 1, 2, 4, 8, ) Μια εσωτερική επανάληψη ταξινομεί τους δύο πίνακες που βρίσκονται μέσα σε ένα block Μέριμνα για τον δεύτερο πίνακα σε περίπτωση που απομένουν λιγότερα στοιχεία από το μέγεθος του block Επανάληψη διαδικασίας μέχρι το μέγεθος του block να γίνει ίσο ή να ξεπεράσει το μέγεθος του αρχικού πίνακα Το πλήθος των αντιστροφών είναι ίσο με το συνολικό άθροισμα αντιστροφών που προκύπτουν σε κάθε συγχώνευση
Επαναληπτική Mergesort (2) static long iterativemergesortandcount(integer[] Array) for (int j = 0; j < right.length; j++) long inversions = 0; // Διπλασιάζουμε σε κάθε πέρασμα το μέγεθος του block_size μέχρι να γίνει // ίσο ή να ξεπεράσει το μήκος του Array. for (int block_size = 1; block_size < Array.length; block_size *= 2) Integer[] Array2 = new Integer[2 * block_size]; Integer[] left = new Integer[block_size]; Integer[] right = new Integer[block_size]; for (int k = 0; k < Array.length; k += 2 * block_size) // Υπολογίζουμε αν ο right χρειάζεται λιγότερες θέσεις λόγω περιττού // πλήθους στοιχείων. int x = Array.length k block_size; // Αν το sum έιναι μικρότερο ή ίσο του μηδέν τότε ο left δεν έχει στοιχεία // οπότε προσπερνάμε την επανάληψη. if (x <= 0) continue; // Περίπτωση όπου χρειαζόμαστε στοιχεία για τον right,αλλά το πλήθος // τους είναι μικρότερο του block_size. if (x < block_size) right = new Integer[x]; // Σπάμε το Array σε 2 άλλα,left και right for (int i = 0; i< left.length; i++) left[i] = Array[i + k]; right[j] = Array[j + k + block_size]; // Συγχωνεύουμε τους πίνακες left και right στον πίνακα Array2. inversions += merge(left, right, Array2); // Αντιγράφουμε τον πίνακα Array2 στον Array. for (int y = 0; y < left.length + right.length; y++) Array[y + k] = Array2[y]; return inversions;
Συνάρτηση merge() (1) Δέχεται ως είσοδο δυο ταξινομημένους πίνακες lft left και right ihtκαι έναν πίνακα ο οποίος θα περιέχει το αποτέλεσμα της συγχώνευσης των left και right Επιστρέφει το πλήθος των αντιστροφών που βρέθηκαν κατά τη διάρκεια της συγχώνευσης Για κάθε πίνακα διατηρούμε έναν δείκτη Διατρέχουμε τους δύο πίνακες επιλέγοντας κάθε φορά το μικρότερο στοιχείο Αυξάνουμε τον δείκτη του πίνακα που επιλέγεται ένα στοιχείο Όταν τελειώσει κάποιος από τους δύο πίνακες, συγχωνεύουμε με τα στοιχεία που έχουν απομείνει στον άλλον Κάθε φορά που επιλέγουμε ένα στοιχείο από τον right πίνακα, έχουμε left.length left_pointer επιπλέον αντιστροφές
Συνάρτηση merge() (2) static long merge(integer [] left, Integer[] right, Integer[] a) long inversions = 0; int i = 0, j = 0, k = 0; while ( (i < left.length) && (j < right.length) ) //Σε αυτήν την περίπτωση δεν υπάρχουν αντιστροφές if (left[i] <= right[j]) a[k] = left[i]; i++; //Σε αυτήν την περίπτωση υπάρχουν τόσες αντιστροφές, //όσες του αριθμού των στοιχείων από τη θέση i και δεξιότερα εντός //του αριστερού υποπίνακα else a[k] = right[j]; j++; inversions += left.length i; k++; //Περίπτωση εξάντλησης των στοιχείων του αριστερού υποπίνακα if(i == left.length) while (j < right.length) a[k] = right[j]; j++; k++; //Περίπτωση εξάντλησης των στοιχείων του δεξιού υποπίνακα else while (i < lfl left.length) a[k] = left[i]; i++; k++; return inversions;
Ανάγνωση Αρχείου Εισόδου Χρήση BufferedReader Χρήση συνάρτησης split() Χρήση συνάρτησης parseint() Μέσα σε try/catch block String InputPath = args[0]; BufferedReader FileInput; FileInput = new BufferedReader(new FileReader(InputPath)); String file_text; file_text = FileInput.readLine(); String[] s = file_text.split(" "); mynumbers = new Integer[s.length]; recmergesort = new Integer[s.length]; itermergesort = new Integer[s.length]; for (int i = 0; i< s.length; i++) mynumbers[i] = Integer.parseInt(s[i]); recmergesort[i] = IntegerparseInt(s[i]); Integer.parseInt(s[i]); itermergesort[i] = Integer.parseInt(s[i]);