Ταξινόμηση με συγχώνευση

Yλοποίηση από πάνω προς τα κάτω

Παράδειγμα σε ψευδοκώδικα για την από πάνω προς τα κάτω του αλγορίθμου ταξινόμησης με συγχώνευση, το οποίο χρησιμοποιεί αναδρομή ώστε να διαιρέσει την λίστα σε υπολίστες, κατόπιν συγχωνεύει τις υπολίστες όσο οι επιστροφές ανεβάζουν το πρόγραμμα στην αλληλουχία των κλήσεων.
συνάρτηση merge_sort(λίστα μ)
    // αν το μέγεθος της λίστας είναι 1, θεώρησέ την ταξινομημένη και επίστρεψέ την
    αν μήκος(μ) <= 1
        επίστρεψε μ
    // αλλιώς το μήκος της λίστας είναι > 1, επομένως χώρισε τη λίστα σε δύο υπολίστες
    μεταβλητή λίστα αριστερά, δεξιά
    μεταβλητή ακέραιος μέσον = μήκος(μ) / 2
    για κάθε x στο μ μέχρι το μέσον
         πρόσθεσε το x στο αριστερά
    για κάθε x στο μ μετά ή ακριβώς στο μέσον
         πρόσθεσε το x στα δεξιά
    // κάλεσε αναδρομικά την merge_sort() ώστε να χωρίσεις περαιτέρω κάθε υπολίστα
    // μέχρι το μέγεθος της υπολίστας να μείνει 1
    αριστερά = merge_sort(αριστερά)
    δεξιά = merge_sort(δεξιά)
    // συγχώνευσε τις υπολίστες που επιστράφηκαν από τις προηγούμενες κλήσεις στην merge_sort()
    // και επίστρεψε την προκύπτουσα συγχωνευμένη υπολίστα
    επέστρεψε merge(αριστερά, δεξιά)
Στο παρόν παράδειγμα, η συνάρτηση merge συγχωνεύει την αριστερή και τη δεξιά υπολίστα.
συνάρτηση merge(αριστερά, δεξιά)
    μεταβλητή λίστα αποτέλεσμα
    όσο μήκος(αριστερά) > 0 ή μήκος(δεξιά) > 0
        αν μήκος(αριστερά) > 0 και μήκος(δεξιά) > 0
            αν πρώτο(αρστερά) <= πρώτο(δεξιά)
                προσάρτησε πρώτο(αριστερά) στο αποτέλεσμα
                αριστερά = υπόλοιπα(αριστερά)
            αλλιώς
                προσάρτησε πρώτο(δεξιά) στο αποτέλεσμα
                δεξιά = υπόλοιπα(δεξιά)
        αλλιώς αν μήκος(αριστερά) > 0
            προσάρτησε πρώτο(αριστερά) στο αποτέλεσμα
            αριστερά = υπόλοιπα(αριστερά)
        αλλιώς αν μήκος(δεξιά) > 0
            προσάρτησε πρώτο(δεξιά) στο αποτέλεσμα
            δεξιά = υπόλοιπα(δεξιά)
    τέλος του όσο
    επέστρεψε αποτέλεσμα

Υλοποίηση από κάτω προς τα πάνω

Παράδειγμα ψευδοκώδικα για την από κάτω προς τα πάνω του αλγορίθμου ταξινόμησης με συγχώνευση, το οποίο μεταχειρίζεται τη λίστα σαν έναν πίνακα από n υπολίστες (εδώ αποκαλούνται αλληλουχίες(runs)) μεγέθους 1, και επαναληπτικά συγχωνεύει τις υπολίστες μπρος και πίσω μεταξύ δύο προσωρινών χώρων:
/* ο πίνακας A[] έχει τα στοιχεία προς ταξινόμηση - ο πίνακας B[] είναι ο πίνακας εργασίας */BottomUpSort(int n, array A[n], array B[n]){
  int width;

  /* each 1-element run in A is already "sorted". */

  /* Δημιούργησε αλλεπάλληλες μακρύτερες αλληλουχίες μήκους 2, 4, 8, 16... μέχρι να ταξινομηθεί όλος ο πίνακας */
  for (width = 1; width < n; width = 2 * width)
    {
      int i;

      /* ο πίνακας A είναι γεμάτος από αλληλουχίες μήκους width */
      for (i = 0; i < n; i = i + 2 * width)
        {
          /* Συγχώνευσε δυο αλληλουχίες: την A[i:i+width-1] και την A[i+width:i+2*width-1] στο B[] */
          /*  ή αντίγραψε την A[i:n-1] στο B[] ( αν(i+width >= n) ) */
          BottomUpMerge(A, i, min(i+width, n), min(i+2*width, n), B);
        }

      /* Τώρα ο πίνακας εργασίας B είναι γεμάτος με αλληλουχίες μήκους 2*width */
      /* αντέγραψε τον πίνακα B στον πίνακα A για την επόμενη επανάληψη */
      /*   μια πιο αποδοτική υλοποίηση θα αντιμετέθετε τους ρόλους του A και του B */
      CopyArray(A, B, n);
      /* τώρα ο πίνακας A είναι γεμάτος με αλληλουχίες μήκους 2*width */
    }}BottomUpMerge(array A[], int iLeft, int iRight, int iEnd, array B[]){
  int i0 = iLeft;
  int i1 = iRight;
  int j;

  /* όσο υπάρχουν στοιχεία στην αριστερή ή στη δεξιά λίστα */
  for (j = iLeft; j < iEnd; j++)
    {
      /* αν η αριστερή λίστα έχει κεφαλή και είναι <= από υπάρχουσα κεφαλή της δεξιάς λίστας */
      if (i0 < iRight && (i1 >= iEnd || A[i0] <= A[i1]))
        {
          B[j] = A[i0];
          i0 = i0 + 1;
        }
      else
        {
          B[j] = A[i1];
          i1 = i1 + 1;
        }
    }}

Ταξινόμηση με φυσική συγχώνευση

Η ταξινόμηση με φυσική συγχώνευση (Natural merge sort) είναι παρόμοια με την από κάτω προς τα πάνω ταξινόμηση συγχώνευσης, με τη διαφορά ότι κάθε φυσικά προκύπτουσα αλληλουχία (ήδη ταξινομημένη ακολουθία) στην είσοδο αξιοποιείται. Στην προσέγγιση από κάτω προς τα πάνω, το σημείο εκκίνησης προϋποθέτει ότι η κάθε αλληλουχία έχει μήκος ενός στοιχείου. Στην πράξη, η τυχαία είσοδος έχει πολλές μικρές αλληλουχίες που τυχαίνει να είναι ήδη ταξινομημένες. Σε μια τυπική περίπτωση, η ταξινόμηση με φυσική συγχώνευση μπορεί να μη χρειαστεί τόσες πολλές επαναλήψεις επειδή υπάρχουν λιγότερες αλληλουχίες να συγχωνευτούν. Για παράδειγμα, στην καλύτερη περίπτωση, η είσοδος είναι ήδη ταξινομημένη (δηλ. είναι μία αλληλουχία), οπότε η ταξινόμηση με φυσική συγχώνευση θα κάνει μόνο μια επανάληψη στα δεδομένα.

Ανάλυση

Στην ταξινόμηση n στοιχείων, η ταξινόμηση με συγχώνευση έχει μέση και χειρότερη επίδοση ίση με O(n log n). Εάν ο χρόνος εκτέλεσης της ταξινόμησησς με συγχώνευση για μια λίστα μήκους n είναι T(n), τότε η επανάληψη T(n) = 2T(n/2) + n προκύπτει από τον ορισμό του αλγορίθμου (εφαρμογή του αλγορίθμου σε δύο λίστες μισού μεγέθους σε σχέση με την αρχική, και προσθήκη των n βημάτων που χρειάζονται για να συγχωνευτούν οι δύο λίστες). Η κλειστή μορφή προκύπτει από το «master theorem».
Στην χειρότερη περίπτωση, ο αριθμός των συγκρίσεων που κάνει η ταξινόμηση με συγχώνευση είναι ίσος ή ελαφρά μικρότερος από (n ⌈lg n⌉ - 2⌈lg ⁿ⌉ + 1), τιμή που βρίσκεται μεταξύ του (n lg n - n + 1) και του (n lg n + n + O(lg n)).
Για μεγάλα n και λίστα εισόδου τυχαία ταξινομημένη, ο αναμενόμενος (μέσος) αριθμός συγκρίσεων προσεγγίζει το α·n, όντας μικρότερος από ό,τι στην χειρότερη περίπτωση όπου 
Στην χειρότερη περίπτωση, η ταξινόμηση με συγχώνευση εκτελεί περίπου 39% λιγότερες συγκρίσεις από όσες η quicksort εκτελεί στη μέση περίπτωση- η ταξινόμηση με συγχώνευση πάντοτε κάνει λιγότερες συγκρίσεις από την quicksort, εκτός από εξαιρετικά σπάνιες περιπτώσεις, όπου η χειρότερη περίπτωση της ταξινόμησης με συγχώνευση ισοδυναμεί με την καλύτερη περίπτωση της quicksort. Όσον αφορά τις μετακινήσεις, η χειρότερη πολυπλοκότητα της ταξινόμησης με συγχώνευση είναι O(n log n)— η ίδια με την καλύτερη της quicksort, ενώ η καλύτερη περίπτωση της ταξινόμησης με συγχώνευση κάνει περίπου τις μισές επαναλήψεις σε σχέση με την χειρότερη περίπτωση.
Οι αναδρομικές υλοποιήσεις της ταξινόμησης με συγχώνευση εκτελούν 2n − 1 κλήσεις μεθόδων στην χειρότερη περίπτωση, σε σύγκριση με τις n της quicksort, οπότε η ταξινόμηση με συγχώνευση έχει σχεδόν διπλάσιο φόρτο αναδρομής από την quicksort. Ωστόσο, οι επαναληπτικές, μη αναδρομικές, υλοποιήσεις της ταξινόμησης με συγχώνευση, οι οποίες αποφεύγουν τον φόρτο των κλήσεων μεθόδων. δεν είναι δύσκολο να γραφούν. Η πιο συνήθης υλοποίηση της ταξινόμησης με συγχώνευση δεν ταξινομεί επί τόπου, επομένως, μνήμη με μέγεθος όσο αυτό της εισόδου πρέπει να δεσμευτεί και για την αποθήκευση της ταξινομημένης εξόδου (δείτε παρακάτω για εκδόσεις που χρειάζονται μόνο n/2 επιπλέον χώρου).
Η επί τόπου ευσταθής ταξινόμηση είναι δυνατή αλλά πιο πολύπλοκη, και συνήθως λίγο πιο αργή, ακόμα και αν ο αλγόριθμος τρέχει σε χρόνο (n log n) (Katajainen, Pasanen & Teuhola 1996). Μια μέθοδος επί τόπου ταξινόμησης με συγχώνευση είναι να συγχωνεύονται τα τμήματα αναδρομικά. Όπως και στην απλή ταξινόμηση με συγχώνευση, η επί τόπου συγχώνευση με ταξινόμηση είναι επίσης ευσταθής ταξινόμηση. Η ευσταθής ταξινόμηση συνδεδεμένων λιστών είναι απλούστερη. Σε αυτήν την περίπτωση, ο αλγόριθμος δεν χρησιμοποιεί επιπλέον χώρο εκτός από αυτόν που είναι ήδη δεσμευμένος για την αναπαράσταση της λίστας, πέρα από τα O(log(k)) που χρησιμοποιούνται για το ίχνος της αναδρομής.
Η ταξινόμηση με συγχώνευση είναι πιο αποδοτική από την quick sort για κάποοιυς τύπους λιστών, εάν τα δεδομένα που πρόκειται να συγχωνευθούν μπορούν να προσπελαστούν μόνο σειριακά, και για αυτό είναι δημοφιλής σε γλώσσες όπως η Lisp, όπου οι σειραϊκά προσπελάσιμες δομές δεδομένων είναι πολύ κοινές. Αντίθετα από κάποιες (αποδοτικές) υλοποιήσεις της quicksort, η ταξινόμηση με συγχώνευση είναι ευσταθής εφόσον η συγχώνευση υλοποιηθεί σωστά.
Η ταξινόμηση με συγχώνευση έχει και κάποια μειονεκτήματα. Ένα από αυτά είναι η χρήση 2n θέσεων μνήμης; οι επιπλέον n θέσεις συνήθως χρησιμοποιούνται επειδή η επί τόπου συγχώνευση δύο ταξινομημένων συνόλων είναι πιο περίπλοκη και θα χρειαζόταν περισσότερες συγκρίσεις και μετακινήσεις. Όμως, παρά την χρησιμοποίηση αυτού του χώρου, ο αλγόριθμος εκτελεί πολλές εργασίες: Τα περιεχόμενα του m αντιγράφονται πρώτα στους πίνακες αριστερά και δεξιά και αργότερα στη λίστα αποτέλεσμα με κάθε κλήση της merge_sort (τα ονόματα των μεταβλητών αντιστοιχούν στον ψευδοκώδικα παραπάνω). Μια εναλλακτική περίπτωση για την αντιγραφή είναι να αντιστοιχιστεί έναν νέο πεδίο για κάθε στοιχείο στο m. Αυτό το πεδίο θα χρησιμοποιείται για να συνδέει τα στοιχεία και οποιεσδήποτε άλλες πληροφορίες σε μια ταξινομημένη λίστα (το στοιχείο μαζί με τις σχετικές πληροφορίες ονομάζεται εγγραφή). Κατόπιν, η συγχώνευση των ταξινομημένων λιστών προχωράει με την αλλαγή της τιμής του συνδέσμου αυτού - δεν χρειάζεται να μετακινηθεί καμία εγγραφή. Ένα πεδίο που περιέχει μόνο το σύνδεσμο θα είναι σε γενικές γραμμές μικρότερο από μια ολόκληρη εγγραφή οπότε θα χρειαστεί και λιγότερος χώρος.
Μια άλλη εναλλακτική για την μείωση του επιπλέον χώρου σε n/2 είναι να διατηρηθούν οι πίνακες αριστερά και δεξιά σα μια συνδυασμένη δομή, να αντιγράφεται μόνο το αριστερό τμήμα του m σε έναν προσωρινό χώρο, και να κατευθύνεται η ρουτίνα merge ώστε να τοποθετεί τη συγχωνευμένη έξοδο στο m. Σε αυτήν την έκδοση είναι προτιμότερο να δεσμεύεται ο προσωρινός χώρος εκτός της ρουτίνας merge, ώστε μόνο μια δέσμευση να είναι απαραίτητη. Η επιπλέον αντιγραφή που αναφέρεται στην προηγούμενη παράγραφο αντιμετωπίζεται επίσης, αφού το τελευταίο ζευγάρι γραμμών πριν από τη δήλωση επέστρεψε αποτέλεσμα (συνάρτηση merge στον ψευδοκώδικα παραπάνω) γίνεται περιττό.
Η ταξινόμηση με συγχώνευση μπορεί να γίνει επίσης συγχωνεύονται περισσότερες από δύο υπολίστες κάθε φορά, χρησιμοποιώντας έναν αλγόριθμο συγχώνευσης ν - δρόμων. Ωστόσο, ο αριθμός των λειτουργιών είναι προσεγγιστικά ο ίδιος. Ας υποθέσουμε ότι συγχωνεύουμε k υπολίστες μονομιάς, όπου χάριν απλότητας το k είναι δύναμη του 2. Τότε η αναδρομική σχέση γίνεται T(n) = k T(n/k) + O(n log k). (Το τελευταίο τμήμα προέρχεται από τον αλγόριθμο συγχώνευσης, οποίος όταν υλοποιείται με τον ιδανικό τρόπο, χρησιμοποιώντας έναν σωρό ή έναν αυτο-ισορροπούμενο δυαδικό δέντρο αναζήτησης, παίρνει O (log k) χρόνο ανά στοιχείο.) Εάν πάρουμε την αναδρομική σχέση για την κλασσική ταξινόμηση με συγχώνευση (T(n) = 2T(n/2) + O(n)) και την αναπτύξουμε log₂k φορές, παίρνουμε την ίδια αναδρομική σχέση. Αυτό ισχύει ακόμα και αν το k δεν είναι σταθερά.

Βελτιστοποίηση ταξινόμησης με συγχώνευση

Στους μοντέρνους υπολογιστές, η τοπικότητα της αναφοράς μπορεί να είναι μεγάλης σημασίας για τη βελτιστοποίηση του λογισμικού, επειδή χρησιμοποιούνται πολυεπίπεδες ιεραρχίες μνήμης. Έχουν προταθεί εκδόσεις του αλγορίθμου που εκμεταλλεύονται την λανθάνουσα μνήμη, οι οποίες αποτελούνται από εντολές βελτιστοποιημένες για την μετακίνηση σελίδων μέσα και έξω από τη λανθάνουσα μνήμη. Για παράδειγμα, η 'ταξινόμηση με συγχώνευση σε τμήματα' σταματάει τον κατακερματισμώ των υπολιστών όταν φτάσει σε υπολίστες μεγέθους S, όπου S ο αριθμός των στοιχείων δεδομένων που χωράνε στην λανθάνουσα μνήμη της Κεντρικής Μονάδας Επεξεργασίας. Κάθε μία από αυτές τις υπολίστες ταξινομείται με έναν επιτόπου αλγόριθμο ταξινόμησης, ώστε να αποδευχθούν οι αντιμεταθέσεις μνήμης, και κατόπιν η τυπική ταξινόμηση με συγχώνευση αναλαμβάνει με τη γνωστή αναδρομική μέθοδο. Αυτός ο αλγόριθμος έχει αποδειχθεί να αποδίδει καλύτερα σε μηχανές που επωφελούνται από την βελτιστοποίηση με λανθάνουσα μνήμη. (LaMarca & Ladner 1997)
Ο Kronrod (1969) πρότεινε μια εναλλακτική έκδοση της ταξινόμησης με συγχώνευση η οποία χρησιμοποιεί σταθερή πρόσθετη μνήμη. Ο αλγόριθμος αυτός αργότερα βελτιώθηκε. (Katajainen, Pasanen & Teuhola 1996).
Επίσης, πολλές εφαρμογές της εξωτερικής ταξινόμησης χρησιμοποιεί μια μορφή ταξινόμησης με συγχώνευση όπου η είσοδος διαχωρίζεται σε έναν αριθμό υπολιστών, για τον οποίον αριθμό, στην ιδανική περίπτωση, η συγχώνευση επιτρέπει το τρέχον σύνολο επεξεργασίας να χωράει στην κύρια μνήμη.

Παράλληλη επεξεργασία

Η ταξινόμηση με συγχώνευση αποτελεί καλή περίπτωση αλγορίθμου ως προς τη δυνατότητά του να εκτελεσθεί παράλληλα, εξαιτίας της μεθόδου διαίρει και βασίλευε που χρησιμοποιεί. Μια παράλληλη υλοποίηση υπάρχει γραμμένη σε ψευδοκώδικα στην τρίτη έκδοση του Introduction to Algorithms των Cormen, Leiserson, και Stein . Αυτός ο αλγόριθμος χρησιμοποιεί έναν παράλληλο αλγόριθμο συγχώνευσης ώστε να εκτελέσει παράλληλα όχι μόνο την αναδρομική διαίρεση του πίνακα, αλλά και την λειτουργία της συγχώνευσης. Αποδίδει καλά στην πράξη όταν συνδυάζεται με μια γρήγορη ευσταθή σειριακή ταξινόμηση, όπως η ταξινόμηση με εισαγωγή και με μια γρήγορη σειριακή συγχώνευση σαν βάση για τη συγχώνευση μικρών πινάκων. Η ταξινόμηση με συγχώνευση ήταν ένας από τους πρώτους αλγορίθμους ταξινόμησης όπου επετεύχθη μια βέλτιστη αύξηση της ταχύτητας, με τον Richard Cole να χρησιμοποιεί έναν έξυπνο αλγόριθμο υποδειγματοληψίας ώστε να καταφέρει συγχώνευση με κόστος O(1). Άλλοι εξεζτητημένοι παράλληλοι αλγόριθμοι ταξινόμησης μπορούν να επιτύχουν τα ίδια ή καλύτερα χρονικά όρια με χαμηλότερη σταθερά. Για παράδειγμα, το 1991 ο David Powers περιέγραψε μια παράλληλη έκδοση της quicksort (και μια σχετική "radix sort") που τρέχει σε χρόνο O(log n) σε ένα CRCW PRAM με n επεξεργαστές εκτελώντας τον κατακερματισμό των πινάκων εσωτερικά.

Σύγκριση με άλλους αλγορίθμους ταξινόμησης

Παρόλο που η ταξινόμηση με σωρό έχει τα ίδια χρονικά φράγματα με την ταξινόμηση με συγχώνευση, απαιτεί μόνο Θ(1) βοηθητικό χώρο, σε σύγκριση με την ταξινόμηση με συγχώνευση, και είναι συχνά γρηγορότερη σε πρακτικές εφαρμογές. Στις τυπικές μοντέρνες αρχιτεκτονικές, οι αποδοτικές υλοποιήσεις της ταχείας ταξινόμησης είναι ταχύτερες της ταξινόμησης με συγχώνευση στην ταξινόμηση πινάκων που βρίσκονται στην κεντρική μνήμη. Από την άλλη μεριά, η ταξινόμηση με συγχώνευση είναι ευσταθής συγχώνευση, υλοποιείται καλύτερα ώς προς τον παραλληλισμό, και είναι πιο αποδοτική στον χειρισμό σειριακών μέσων με αργή προσπέλαση. Η ταξινόμηση με συγχώνευση είναι συνήθως η καλύτερη επιλογή για την ταξινόμηση μιας συνδεδεμένης λίστας: σε αυτήν την περίπτωση είναι σχετικά εύκολο να υλοποιηθεί μια ταξινόμηση με συγχώνευση με τρόπο τέτοιο που να απαιτεί μόνο Θ(1) επιπλέον χώρο, και η χαμηλή απόδοση τυχαίας προσπέλασης της συνδεδεμένης λίστας κάνει κάποιους αλγορίθμους (όπως της ταχείας ταξινόμησης) να μην αποδίδουν ικανοποιητικά, ενώ κάποιοι άλλοι (όπως η ταξινόμηση με σωρό) είναι αδύνατο να υλοποιηθούν.
Από την Perl 5.8, η ταξινόμηση με συγχώνευση είναι ο προεπιλεγμένος αλγόριθμος με συγχώνευση (στις πρηγούμενες εκδόσεις της Perl ήταν η ταχεία ταξινόμηση). Στην Java, οι Arrays.sort() χρησιμοποιούν την ταξινόμηση με συγχώνευση ή μια βελτιωμένη ταχεία ταξινόμηση, σε συνάρτηση με τους τύπους των δεδομένων και για λόγους απόδοσης της υλοποίησης αλλάζουν σε ταξινόμηση με εισαγωγή όταν ταξινομούνται λιγότερα από επτά στοιχεία. Η Python χρησιμοποιεί την timsort, μια άλλη βελτιωμένη υβριδική έκδοση της ταξινόμησης με συγχώνευση και της ταξινόμησης με εισαγωγή, η οποία επίσης έγινε η προεπιλεγμένη μέθοδος ταξινόμησης για την Java SE 7.

Χρησιμότητα στην ταξινόμηση στο Διαδίκτυο

Η λειτουργία της συγχώνευσης στην ταξινόμηση με συγχώνευση είναι χρήσιμη στην ταξινόμηση στο Διαδίκτυο, όπου η λίστα προς ταξινόμηση λαμβάνεται στοιχείο προς στοιχείο, αντί να ληφθεί ολόκληρη από την αρχή. Σε αυτήν την εφαρμογή, ταξινομούμε κάθε νέο στοιχείο που λαμβάνουμε χρησιμοποιώντας οποιονδήποτε αλγόριθμο ταξινόμησης, και μετά το συγχωνεύουμε στην μέχρι στιγμής ταξινομημένη μας λίστα χρησιμοποιώντας τη λειτουργία της συγχώνευσης. Ωστόσο, η προσέγγιση αυτή μπορεί να αποδειχθεί ακριβή σε χρόνο και χώρο εάν τα δεδομένα λαμβάνονται σε μικρά τμήματα σε σχέση με την ταξινομημένη λίστα — μια καλύτερη προσέγγιση σε αυτήν την περίπτωση είναι η εισαγωγή των στοιχείων σε ένα δυαδικό δένδρο αναζήτησης τη στιγμή που λαμβάνονται.