8-1 ΜΑΘΗΜΑ 8 ο Εισαγωγή στους Αλγόριθμους Ταξινόμησης
8-2 ΕΙΣΑΓΩΓΗ ΣΤΗ ΤΑΞΙΝΟΜΗΣΗ Ένας μεγάλος αριθμός προβλημάτων και αλγορίθμων που τα επιλύουν απαιτούν την ύπαρξη δεδομένων που έχουν ταξινομηθεί κατά κάποιο τρόπο Η πιο απλή μορφή ταξινόμησης είναι τα δεδομένα να ευρίσκονται σε αύξουσα ή φθίνουσα μορφή σύμφωνα με συγκεκριμένα κριτήρια Το αντικείμενο αυτής της ενότητας μαθημάτων είναι η κατανόηση και ανάλυση βασικών αλγορίθμων ταξινόμησης
8-3 ΑΛΓΟΡΙΘΜΟΙ ΤΑΞΙΝΟΜΗΣΗΣ Οι αλγόριθμοι ταξινόμησης που θα εξετάσουμε είναι: –Insertion Sort –Selection Sort –Bubble Sort –Quick Sort –Merge Sort
8-4 INSERTION SORT Η ιδέα είναι δεδομένης μίας ακολουθίας Α να υπολογίσουμε μια σειρά από ταξινομημένες ακολουθίες S 0, S 1, S 2, …. S n όπου: –Η πρώτη ακολουθία είναι η S 0 = {} –Δεδομένης της ακολουθίας S i 0 ≤ i < n, η επόμενη ακολουθία S i+1 υπολογίζεται τοποθετώντας το (i + 1) th στοιχείο της μη ταξινομημένης ακολουθίας Α στο σωστό σημείο της S i
8-5 Ο Αλγόριθμος INSERTION SORT void insertionSort(int arrayData[], arraySize) { int a; int b; int temp; for(a=1; a<arraySize; a++) b = a; while(arrayData[b] 0) { temp = arrayData[b-1]; arrayData[b-1] = arrayData[b]; arrayData[b] = temp; b--; }
8-6 ΠΑΡΑΔΕΙΓΜΑ INSERTION SORTING void insertionSort(int arrayData[], arraySize) { int a; int b; int temp; for(a=1; a<arraySize; a++) b = a; while(arrayData[b] 0) { temp = arrayData[b-1]; arrayData[b-1] = arrayData[b]; arrayData[b] = temp; b--; } a=1 a=2 a=3 a=4 a=5 a=6 a=7 a=8 a=9
8-7 SELECTION SORT Το Selection Sort δουλεύει ώς εξής: –Δεδομένης μίας ακολουθίας Α, κατασκευάζουμε την ταξινομημένη ακολουθία Τ, ένα στοιχείο κάθε φορά –Εισάγουμε τα νέα στοιχεία στο σωστό σημείο –Κάθε φορά, επιλέγουμε το νέο στοιχείο από αυτά που δεν έχουμε ακόμη επιλέξει από την Α –Σε κάθε βήμα του αλγόριθμου μια γραμμική σάρωση των μη ταξινομημένων στοιχείων της λίστας μας δίνει το επόμενο μεγαλύτερο (ή μικρότερο εάν θέλουμε φθίνουσα ταξινόμηση). –Αυτό το στοιχείο τοποθετείται στη κατάλληλη θέση
8-8 Ο Αλγόριθμος SELECTION SORT void StraightSelectionSort(int arrayData[], int arraySize) { int i, j, temp; int max=0; for (i=arraySize; i>1; i--) { max = 0; for(j=1; j<i, j++) { if(arrayData[j] > arrayData[max]) max =j; temp = arrayData[max]; arrayData[max] = arrayData[i-1]; arrayData[i-1] = temp; }
8-9 ΠΑΡΑΔΕΙΓΜΑ STRAIGHT SELECTION SORTING void StraightSelectionSort(int arrayData[], int arraySize) { int i, j, temp; int max=0; for (i=arraySize; i>1; i--) { max = 0; for(j=1; j<i, j++) { if(arrayData[j] > arrayData[max]) max =j; } temp = arrayData[max]; arrayData[max] = arrayData[i-1]; arrayData[i-1] = temp; } i=10 i=9 i=8 i=7 i=6 i=5 i=4 i=3 i=2
8-10 BUBBLE SORT void bubbleSort(int arrayData[], int arraySize) { int i; int j; int temp; for (i= arraySize, i > 1; i--) for (j = 0; j < i - 1; j++) if(array[j] > array[j+1]) temp = arrayData[j]; arrayData[j] = arrayData[j+1]; arrayData[j+1] = temp; }
8-11 ΠΑΡΑΔΕΙΓΜΑ BUBBLE SORTING void bubbleSort(int arrayData[], int arraySize) { int i; int j; int temp; for (i= arraySize, i > 1; i--) for (j = 0; j < i - 1; j++) if(array[j] > array[j+1]) temp = arrayData[j]; arrayData[j] = arrayData[j+1]; arrayData[j+1] = temp; } i=10 i=9 i=8 i=7 i=6 i=5 i=4 i=3 i=2
8-12 QUICK SORT Δεδομένης της ακολουθίας (διανύσματος) Α = [α 1, α 2, α 3,...α ν ], το ζητούμενο είναι να την ταξινομήσουμε π.χ. με αύξουσα σειρά. Ο αλγόριθμος σκιαγραφείται ως εξής: –Διαλέγουμε ένα στοιχείο p το οποίο ονομάζουμε pivot και p = α κ –Αφαιρούμε το στοιχείο p, και χωρίζουμε την ακολουθία Α σε δύο υπο-ακολουθίες L και G, τέτοιες ώστε όλα τα στοιχεία στην L είναι μικρότερα του p, και όλα τα στοιχεία στην G, είναι μεγαλύτερα του p. –Αναδρομικά, ταξινομούμε τις υπο-ακολουθίες L και G.
8-13 Ο Αλγόριθμος QUICK SORT void QuickSort(int arrayData[], int left, int right) { int i, j, p, temp; i = left; j = right; p = arrayData((left+right)/2); do { while(arrayData[i] < p && i < right) i++; while(p left) j--; if(i <=j) { temp = arrayData[i]; arrayData[i] = arrayData[j]; arrayData[j] = temp; i++; j--; } while i <= j); if(left <j) QuickSort(arrayData, left, j); if(i < right) QuickSort(arrayData, i, right); }
8-14 ΠΑΡΑΔΕΙΓΜΑ QUICK SORTING void QuickSort(int arrayData[], int left, int right) { int i, j, p, temp; i = left; j = right; p = arrayData[(left+right)/2]; do { while(arrayData[i] < p && i < right) i++; while(p left) j--; if(i <=j) { temp = arrayData[i]; arrayData[i] = arrayData[j]; arrayData[j] = temp; i++; j--; } } while i <= j); if(left < j) QuickSort(arrayData, left, j); if(i < right) QuickSort(arrayData, i, right); } i=0, j=7, p=1 i=0, j=3 i=2, j=0 i=2, j=7, p=5 ………
8-15 MERGE SORT Για να ταξινομήσουμε μία ακολουθία Α από κ > 1 στοιχεία δουλεύουμε ώς εξής: –Χωρίζουμε την ακολουθία Α σε δύο «ίσα» μέρη –Αναδρομικά ταξινομούμε τις υπο-ακολουθίες, και –Ενοποιούμε τις δύο ταξινομημένες υπο-ακολουθίες
8-16 Ο Αλγόριθμος MERGE SORT void merge_sort (float v[], int start, int end) { int middle; /* the middle of the subarray */ if (start == end) return; if (start == end - 1) return; middle = (start + end) / 2; merge_sort (v, start, middle); merge_sort (v, middle, end); merge (v, start, middle, end); } void merge (float v[], int start, int middle, int end) { int v1_n, v2_n, v1_index, v2_index, i; int v1_n, v2_n, v1_index, v2_index, i; v1_n = middle - start; v1_n = middle - start; v2_n = end - middle; v2_n = end - middle; float v1[v1_n]; float v2[v2_n]; float v1[v1_n]; float v2[v2_n]; for (i=0; i<v1_n; i++) v1[i] = v[start + i]; for (i=0; i<v1_n; i++) v1[i] = v[start + i]; for (i=0; i<v2_n; i++) v2[i] = v[middle + i]; for (i=0; i<v2_n; i++) v2[i] = v[middle + i]; v1_index = 0; v2_index = 0; v1_index = 0; v2_index = 0; for (i=0; (v1_index < v1_n) && (v2_index < v2_n); i++) for (i=0; (v1_index < v1_n) && (v2_index < v2_n); i++) { if (v1[v1_index] <= v2[v2_index]) if (v1[v1_index] <= v2[v2_index]) v[start + i] = v1[v1_index++]; v[start + i] = v1[v1_index++]; else else v[start + i] = v2[v2_index++]; v[start + i] = v2[v2_index++]; } for (; v1_index < v1_n; i++) for (; v1_index < v1_n; i++) [start + i] = v1[v1_index++]; [start + i] = v1[v1_index++]; for (; v2_index < v2_n; i++) for (; v2_index < v2_n; i++) v[start + i] = v2[v2_index++]; v[start + i] = v2[v2_index++]; }
8-17 ΠΑΡΑΔΕΙΓΜΑ MERGE SORTING k j i Two-way merging
8-18 ΠΑΡΑΔΕΙΓΜΑ MERGE SORTING
8-19 ΑΝΑΛΥΣΗ ΑΛΓΟΡΙΘΜΩΝ ΤΑΞΙΝΟΜΗΣΗΣ Η Ταξινόμηση είναι ένα από τα βασικά πρoβλήματα στην επιστήμη υπολογιστών (Computer Science) Υπάρχουν πολλοί αλγόριθμοι ταξινόμησης άλλοι απλοί (π.χ. Bubble sort) και άλλοι πολύπλοκοι (π.χ. Quick Sort, Heap sort) Οι περισσότεροι αλγόριθμοι ταξινόμησης μπορούν να εμπέσουν σε δύο κατηγορίες όσον αφορά την χρονική πολυπλοκότητα τους (σχέση με ταχύτητα εκτέλεσης τους και παραγωγής αποτελέσματος): 1.Αλγόριθμοι με πολυπλοκότητα Ο(n 2 ) (bubble, insertion, selection, shell sort) 2.Αλγόριθμοι με πολυπλοκότητα Ο(n log(n)) (heap, quick, merge sort) Η χρονική πολυπλοκότητα που ορίζεται με την έκφραση Ο() είναι ένα μέτρο του πόσο γρήγορα εκτελεί ένας αλγόριθμος τη λειτουργία του σαν συνάρτηση του όγκου των δεδομένων που χρειάζεται
8-20 ΑΝΑΛΥΣΗ ΑΛΓΟΡΙΘΜΩΝ ΤΑΞΙΝΟΜΗΣΗΣ Όπως είπαμε η πολυπλοκότητα είναι ένα χαρακτηριστικό στοιχείο ενός αλγόριθμου. Έχουμε χρονική πολυπλοκότητα (time complexity) και πολυπλοκότητα ανάγκης σε μνήμη (space complexity) Η κατηγοριοποίηση της πολυπλοκότητας (χρονικής ή απαραίτητης μνήμης) σε απλές μορφές (από την καλύτερη προς τη χειρότερη) είναι: –Ο(1): Σταθερή (constant) χρονική (ή μνήμης) πολυπλοκότητα. Ο αλγλοριθμος παίρνει τον ίδιο χρόνο (ή μνήμη) να παράγει αποτέλεσμα ανεξάρτητα από τον όγκο των δεδομένων που επεξεργάζεται –O(log(n)): Λογαριθμική (logarithmic) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει τετραγωνική λογαριθμική σχέση με τον όγκο των δεδομένων –O(log 2 (n)): Λογαριθμική (logarithmic) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει λογαριθμική σχέση με τον όγκο των δεδομένων –Ο(n): Γραμμική (linear) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει γραμμική σχέση με τον όγκο των δεδομένων –Ο(n log(n)): Γραμμικά-λογαριθμική (n log(n) ) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει σχέση με τον όγκο των δεδομένων που δίδεται από τη συνάρτηση n log(n) –Ο(n 2 ): Τετραγωνική (quadratic) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει τετραγωνική σχέση με τον όγκο των δεδομένων –O(n 3 ): Κυβική (cubic) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει κυβική σχέση με τον όγκο των δεδομένων –Ο(2 n ): Εκθετική (exponential) χρονική (ή μνήμης) πολυπλοκότητα. Ο χρόνος εκτέλεσης (ή η ανάγκες σε μνήμη) έχει εκθετική σχέση με τον όγκο των δεδομένων
8-21 ΑΝΑΛΥΣΗ ΑΛΓΟΡΙΘΜΩΝ ΤΑΞΙΝΟΜΗΣΗΣ Για να δούμε τη διαφορά μεταξύ των διαφόρων κατηγοριών πολυπλοκότητας ας θεωρήσουμε ότι έχουμε να ταξινομήσουμε 100, και 1000 στοιχεία Ένας αλγόριθμος γραμμικής σταθερής πολυπλοκότητας θα πάρει περίπου 1 χρονική μονάδα να παράγει αποτέλεσμα και για τα 100 και για τα 1000 στοιχεία Ένας αλγόριθμος λογαριθμικής χρονικής πολυπλοκότητας θα πάρει περίπου 2 χρονικές μονάδες να παράγει αποτέλεσμα για τα 100 στοιχεία, και 3 χρονικές μονάδες να παράγει αποτέλεσμα για τα 1000 στοιχεία Ένας αλγόριθμος γραμμικής χρονικής πολυπλοκότητας θα πάρει περίπου 100 χρονικές μονάδες να παράγει αποτέλεσμα για τα 100 στοιχεία και 1000 για τα 1000 στοιχεία Ένας αλγόριθμος τετραγωνικής χρονικής πολυπλοκότητας θα πάρει περίπου χρονικές μονάδες να παράγει αποτέλεσμα για τα 100 στοιχεία και για τα 1000 στοιχεία Ένας αλγόριθμος κυβικής χρονικής πολυπλοκότητας θα πάρει περίπου χρονικές μονάδες να παράγει αποτέλεσμα για τα 100 στοιχεία και για τα 1000 στοιχεία Ένας αλγόριθμος εκθετικής πολυπλοκότητας θα πάρει περίπου χρονικές μονάδες να παράγει αποτέλεσμα για τα 100 στοιχεία και e+301 για 1000 στοιχεία Εάν ένας αλγόριθμος παίρνει πάνω από ένα τύπο δεδομένων σαν είσοδο μπορούμε να έχουμε εκφράσεις πολυπλοκότητας της μορφής Ο(n m) ή O(n logm) όπου n, m είναι το πλήθος των δύο τύπων των δεδομένων αντίστοιχα
8-22 ΕΜΠΕΙΡΙΚΑ ΔΕΔΟΜΕΝΑ Αλγόριθμοι Ταξινόμησης με τετραγωνική Πολυπλοκότητα (O n 2 ) Αλγόριθμοι Ταξινόμησης με γραμμικά λογαριθμική Πολυπλοκότητα O(n log(n))
8-23 Insertion Sort Γενικά: Ο αλγόριθμος βάζει σε κάθε βήμα ένα στοιχείο από την αρχική (μη ταξινομημένη) λίστα στη σωστή του θέση στη τελική ταξινομημένη λίστα Υπέρ: Εύκολος στην υλοποίηση Κατά: Αργός για μεγάλες λίστες
8-24 Insertion Sort Γενικά: Ο αλγόριθμος βάζει σε κάθε βήμα ένα στοιχείο από την αρχική (μη ταξινομημένη) λίστα στη σωστή του θέση στη τελική ταξινομημένη λίστα Υπέρ: Εύκολος στην υλοποίηση Κατά: Αργός για μεγάλες λίστες Χρήση: Είναι ένας αλγόριθμος «μέσης-λύσης» που είναι καλός για ταξινόμηση μερικών χιλιάδων δεδομένων. Ο insertion sort είναι δύο φορές πιο γρήγορος από τον bubble sort και περίπου 40% πιο γρήγορος από τον selection sort. O insertion sort δεν ενδείκνυται να χρησιμοποιείται για την ταξινόμηση περισσότερων από δύο χιλιάδες στοιχεία ή για την διαδοχική και επαναληπτική ταξινόμηση λιστών με περισσότερα από μερικές εκατοντάδες στοιχείων
8-25 Selection Sort Γενικά: Ο αλγόριθμος βάζει σε κάθε βήμα βρίσκει το μικρότερο (ή μεγαλύτερο) στοιχείο που έχει μείνει στην αρχική λίστα, και το βάζει σαν επόμενο στοιχείο στην τελική λίστα Υπέρ: Εύκολος στην υλοποίηση Κατά: Αργός για μεγάλες λίστες Χρήση: Είναι ένας αλγόριθμος πολύ παρόμοιος με τον insertion sort, αλλά πιο αργός οπότε ο insertion sort είναι καλύτερη επιλογή. Ο selection sort είναι περίπου 60% πιο γρήγορος από τον bubble sort (όμως θυμηθείτε ότι ο insertion sort είναι 100% πιο γρήγορος από τον bubble sort). Γενικά δεν υπάρχει καλός λόγος να προτιμήσουμε τον selection sort αλλά εάν θελήσουμε να τον χρησιμοποιήσουμε δεν πρέπει να τον χρησιμοποιήσουμε για ταξινόμηση περισσότερων από 1000 στοιχεία ή γα την επαναλυπτική ταξονόμηση λιστών με περισσότερα από 200 στοιχεία.
8-26 Bubble Sort Γενικά: Ο αλγόριθμος βάζει σε κάθε βήμα βρίσκει ελέγχει εάν το επόμενό του στοιχείο είναι μικρότερο (ή μεγαλύτερο) και εναλλάσσονται εάν χρειάζεται Υπέρ: Εύκολος στην υλοποίηση Κατά: Πολύ αργός για μεσαίες και μεγάλες λίστες. Χρήση: Είναι ένας αλγόριθμος πολύ αργός εκτός και εάν η λίστα δεν είναι πολύ μεγάλη και ήδη σχεδόν ή κατα το πλείστον ταξινομημένη Ο(n)!. Δεν πρέπει να τον χρησιμοποιήσουμε για ταξινόμηση περισσότερων από 100 στοιχεία,ή για την επαναληπτική ταξινόμηση λιστών με περισσότερα από μερικές δεκάδες στοιχείων.
8-27 Quick Sort Γενικά: O Αλγόριθμος είναι ένας «Διαίρει και Βασίλευε» τύπος αλγόριθμου. Είναι μία πιο γρήγορη παραλλαγή του merge sort. Ο αλγόριθμος έχει τέσσερα βήματα: 1.Εάν η λίστα έχει ένα στοιχείο επιστρέφει το στοιχείο 2.Αλλιώς, διαλέγει ένα στοιχείο (pivot) 3.Χωρίζει τη λίστα σε δύο υπο-λίστες μία με στοιχεία μικρότερα ή ίσα του pivot, και μία με στοιχεία μεγαλύτερα του pivot 4.Ταξινομεί αναδρομικά τις δύο υπο-λίστες Γενικά η ταχύτητα του αλγόριθμου επηρεάζεται από την επιλογή του pivot. Η χειρότερη περίπτωση O(n 2 ) είναι όταν η αρχική λίστα είναι ήδη ταξινομημένη και το πρώτο στοιχείο επιλέγεται σαν pivot. Εάν η επιλογή του pivot γίνει τυχαία τότε ο αλγόριθμος έχει μέση χρονική πολυπλοκότητα O(n log(n)), όπου n είναι το πλήθος των στοιχείων της λίστας που θέλουμε να ταξινομήσουμε
8-28 Quick Sort Υπέρ: Πολύ γρήγορος Κατά: Σχετικά μεγάλη κατανάλωση μνήμης λόγω αναδρομής. Κακή σχετικά ακή ταχύτητα όταν η αρχική λίστα είναι ήδη ταξινομημένη Χρήση: Είναι γενικά ένας πολύ καλός αλγόριθμος από άποψη ταχύτητας. Καλός να χρησιμοποιείται για μέτρια και μεγάλα δεδομένα τα οποία δεν είναι ήδη ταξινομημένα. Δεν έχει κάποιο πλεονέκτημα εάν χρησιμοποιηθεί για πολύ μικρές λίστες (μέχρι 100 στοιχεία). Ο quick sort είναι λίγο-πολύ η πρώτη μας επιλογή για μεγάλα δεδομένα τα οποία δεν είναι ήδη ταξινομημένα και εάν δεν θέλουμε να χρησιμοποιήσουμε άλλους αλγόριθμους π.χ. heap sort
8-29 Merge Sort Γενικά: Ο αλγόριθμος χωρίζει τη τη ταξινομημένη λίστα σε δύο ίσες υπο- λίστες και ταξινομεί αναδρομικά την κάθε μια. Στο τέλος συνδυάζει τις δύο ταξινομημένες υπο-λίστες σε μία. Υπέρ: Σχετικά γρήγορος Κατά: Σχετικά μεγάλη κατανάλωση μνήμης λόγω αναδρομής Χρήση: Είναι σχετικά πιο γρήγορος για μεγάλα δεδομένα από το heap sort αλλά απαιτεί διπλάσια μνήμη. Ο heap sort είναι καλύτερο όμως από τον merge sort για πολύ μεγάλα δεδομένα. Γενικά ο merge sort είναι χειρότερος από τον quick sort και είναι κακή επιλογή όταν η μνήμη είναι περιορισμένη