L13 · Άπληστοι Αλγόριθμοι III — Κωδικοποίηση Huffman
Τι θα δούμε
Ένα μόνο πρόβλημα — αλλά από τα ωραιότερα του μαθήματος: πώς συμπιέζεις δεδομένα στο ελάχιστο δυνατό μήκος; Η απάντηση, η κωδικοποίηση Huffman, είναι ένας καθαρός άπληστος αλγόριθμος με μια κομψή απόδειξη βελτιστότητας.
Όπως κάθε άπληστος αλγόριθμος, η Huffman κάνει σε κάθε βήμα την κίνηση που φαίνεται καλύτερη τοπικά, ελπίζοντας ότι θα βγει βέλτιστη συνολικά. Στο L11 όμως είδαμε ότι οι περισσότεροι εύλογοι άπληστοι κανόνες είναι λάθος. Άρα το ζουμί δεν είναι μόνο ο αλγόριθμος — είναι η απόδειξη ότι αυτή τη φορά η άπληστη επιλογή πετυχαίνει. Και η απόδειξη χρησιμοποιεί επιχείρημα ανταλλαγής, ακριβώς την τεχνική του L12.
Το πρόβλημα
Είσοδος: μια ακολουθία δεδομένων — π.χ. το κείμενο ενός email — με διαφορετικούς χαρακτήρες, και για καθέναν τη συχνότητα εμφάνισής του (πόσες φορές εμφανίζεται μέσα στο κείμενο).
Στόχος: ανάθεσε σε κάθε χαρακτήρα μια συμβολοσειρά από δυφία (bits) , ώστε το συνολικό κωδικοποιημένο κείμενο να έχει το συντομότερο δυνατό μήκος.
Σταθερό vs μεταβλητό μήκος
Υπάρχουν δύο τρόποι να δώσεις κώδικες στους χαρακτήρες.
Σταθερό μήκος: δώσε σε κάθε χαρακτήρα τον ίδιο αριθμό δυφίων. Πόσους χαρακτήρες χωράς με δυφία; Κάθε δυφίο έχει 2 δυνατές τιμές, άρα δυφία δίνουν διαφορετικούς συνδυασμούς — δηλαδή κωδικοποιείς χαρακτήρες. Η αποκωδικοποίηση είναι παιχνιδάκι — κόβεις τη συμβολοσειρά ανά δυφία — αλλά δεν πετυχαίνει ελάχιστο μήκος.
Μεταβλητό μήκος: δώσε λίγα δυφία στους συχνούς χαρακτήρες και περισσότερα στους σπάνιους. Έτσι το κομμάτι του κειμένου που εμφανίζεται ξανά και ξανά πληρώνεται φθηνά. Πετυχαίνει ελάχιστο μήκος — με τίμημα μια πιο σύνθετη κωδικοποίηση.
Αλλά πότε αξίζει στ' αλήθεια το μεταβλητό μήκος; Το εργαστήριο παρακάτω χτίζει ζωντανά τη βέλτιστη μεταβλητού μήκους κωδικοποίηση (θα δούμε σε λίγο πώς τη βρίσκουμε) και τη συγκρίνει με το σταθερό μήκος. Σύρε τις συχνότητες: όταν είναι όλες ίσες, το κέρδος σχεδόν εξαφανίζεται· όταν μια συχνότητα κυριαρχεί, εκτοξεύεται.
Σύρε τις συχνότητες των 6 χαρακτήρων. Η Huffman ξαναϋπολογίζεται ζωντανά — δες πότε το μεταβλητό μήκος κερδίζει πολύ και πότε λίγο.
| Χαρ. | Συχνότητα | Μήκος Huffman | Δυφία (f × μήκος) |
|---|---|---|---|
| a | 45 | 1 | 45 |
| b | 13 | 3 | 39 |
| c | 12 | 3 | 36 |
| d | 16 | 3 | 48 |
| e | 9 | 4 | 36 |
| f | 5 | 4 | 20 |
| Σ | 100 | — | 224 |
Το συμπέρασμα: το μεταβλητό μήκος δεν είναι μαγικό. Είναι η ανταμοιβή της λοξότητας — όσο πιο άνισες οι συχνότητες, τόσο περισσότερο έχεις να κερδίσεις.
Η παγίδα της αποκωδικοποίησης
Το μεταβλητό μήκος κρύβει έναν κίνδυνο. Έστω αλφάβητο με κώδικες , , . Σε ποια λέξη αντιστοιχεί η συμβολοσειρά 0101;
Διφορούμενο — δύο διαφορετικές λέξεις, η ίδια συμβολοσειρά. Η αιτία: ο κώδικας του (01) είναι πρόθεμα του κώδικα του (010). Αν το επιτρέψουμε, ο αποκωδικοποιητής δεν ξέρει πού τελειώνει ο ένας χαρακτήρας — διαβάζοντας 01, μπορεί να σταματήσει (βρήκε a) ή να συνεχίσει (ίσως έρχεται b).
Με απροθεματικό κώδικα η αποκωδικοποίηση γίνεται μονοσήμαντη. Π.χ. με , η συμβολοσειρά 11010001010001 διαβάζεται με έναν μόνο τρόπο — ως abfeed.
Κόστος μιας κωδικοποίησης
Κάθε χαρακτήρας συνεισφέρει «πόσο συχνά εμφανίζεται» επί «πόσο μακρύς είναι ο κώδικάς του». Γι' αυτό συμφέρει οι συχνοί χαρακτήρες να έχουν κοντούς κώδικες: πολλαπλασιάζονται με μεγάλο .
Κωδικοποιήσεις ως δυαδικά δέντρα
Εδώ έρχεται η ιδέα-κλειδί: κάθε απροθεματική κωδικοποίηση αντιστοιχεί σε ένα δυαδικό δέντρο.
- Οι χαρακτήρες κάθονται στα φύλλα.
- Το μονοπάτι από τη ρίζα στο φύλλο δίνει τον κώδικα:
0= αριστερό παιδί,1= δεξί παιδί.
Σε αυτή τη γλώσσα, το είναι ακριβώς το βάθος του φύλλου του (πόσες ακμές από τη ρίζα). Άρα το πρόβλημα γίνεται: βρες δυαδικό δέντρο που ελαχιστοποιεί το — το σταθμισμένο βάθος των φύλλων.
Το παρακάτω εργαλείο δένει μαζί και τα τρία: την παγίδα της αμφισημίας, την απροθεματικότητα, και τα δέντρα. Στην πρώτη καρτέλα ο κώδικας δεν είναι απροθεματικός — δες πώς, στο δέντρο, ο χαρακτήρας a δεν κάθεται σε φύλλο, και πώς η ίδια συμβολοσειρά διαβάζεται με δύο τρόπους. Στη δεύτερη, ένας σωστός απροθεματικός κώδικας διαβάζεται καθαρά:
Διάβασε τα δυφία ένα-ένα ακολουθώντας το δέντρο από τη ρίζα. Κώδικας: a→01 · b→010 · c→1
Ο άπληστος αλγόριθμος
- Είσοδος:
- n χαρακτήρες με τις συχνότητές τους
- Έξοδος:
- ένα δυαδικό δέντρο που ορίζει βέλτιστη απροθεματική κωδικοποίηση
Με λόγια. Βάλε κάθε χαρακτήρα ως ένα μονοκόμβο δέντρο σε μια ουρά προτεραιότητας, με κλειδί τη συχνότητα. Επαναλαμβανόμενα: βγάλε τα δύο δέντρα με τη μικρότερη συχνότητα, ένωσέ τα κάτω από έναν νέο κόμβο με συχνότητα το άθροισμά τους, και βάλε το νέο δέντρο πίσω στην ουρά. Όταν μείνει ένα δέντρο, αυτό ορίζει την κωδικοποίηση.
Δες τον να χτίζει το δέντρο. Πρόσεξε το pool να συρρικνώνεται και τους κωδικούς να εμφανίζονται στο τέλος — κοντούς για τους συχνούς χαρακτήρες, μακριούς για τους σπάνιους:
Το δέντρο χτίζεται από κάτω προς τα πάνω· οι κωδικοί εμφανίζονται στο τέλος.
Πολυπλοκότητα
Ένα δυαδικό δέντρο με φύλλα, όπου κάθε εσωτερική κορυφή έχει 2 παιδιά, έχει ακριβώς εσωτερικές κορυφές. Κάθε εσωτερική κορυφή γεννιέται από μία συγχώνευση — άρα ο αλγόριθμος κάνει συγχωνεύσεις. Κάθε συγχώνευση χρειάζεται τα δύο δέντρα με τη μικρότερη συχνότητα: με μια ουρά προτεραιότητας (L10), κάθε extractMin και insert κοστίζει . Συνολικά:
Γιατί είναι βέλτιστη
Έχουμε έναν αλγόριθμο που μοιάζει λογικός. Αλλά «μοιάζει λογικός» δεν αρκεί. Πρέπει να αποδείξουμε ότι η Huffman δίνει πάντα κωδικοποίηση ελάχιστου κόστους — για κάθε αλφάβητο και κάθε συχνότητες.
Η απόδειξη χτίζεται σε τρία κομμάτια: δύο λήμματα που περιγράφουν πώς μοιάζει ένα βέλτιστο δέντρο, και μια επαγωγή που τα συνδυάζει. Πάμε ένα-ένα, πάντα με την εικόνα πριν τον τύπο.
Λήμμα 1 — ο σπανιότερος κάθεται βαθύτερα
Πρώτα η εικόνα. Φαντάσου ένα δέντρο όπου ένας σπάνιος χαρακτήρας έχει κοντό κώδικα (κάθεται ψηλά) ενώ ένας συχνός χαρακτήρας έχει μακρύ κώδικα (κάθεται βαθιά). Αυτό είναι σπατάλη: ο συχνός χαρακτήρας πληρώνει το μακρύ του κώδικα πάρα πολλές φορές, ο σπάνιος πληρώνει τον κοντό του ελάχιστες. Αν τους ανταλλάξουμε τις θέσεις, το συνολικό κόστος πέφτει. Άρα ένα τέτοιο δέντρο δεν ήταν βέλτιστο.
Πιάσε δύο φύλλα και αντάλλαξέ τα. Κάθε ανταλλαγή που μειώνει το κόστος είναι ακριβώς μια τέτοια «αντιστροφή» — ένας συχνός χαρακτήρας θαμμένος πιο βαθιά από έναν σπάνιο:
Κάνε κλικ σε δύο φύλλα και αντάλλαξέ τα. Στόχος: βρες την ανάθεση με το ελάχιστο κόστος (65).
Ψάξε για μια αντιστροφή: δύο φύλλα όπου ο συχνότερος χαρακτήρας κάθεται βαθύτερα από τον σπανιότερο. Αντάλλαξέ τα και δες το κόστος.
Τώρα ο τύπος — απλώς μετράει αυτό που μόλις είδες. Έστω, για άτοπο (υποθέτουμε το αντίθετο και ψάχνουμε παραλογισμό), ότι αλλά . Ανταλλάσσουμε τα φύλλα των και , παίρνοντας κωδικοποίηση . Η διαφορά κόστους:
— γινόμενο δύο αρνητικών ποσοτήτων ( και ), άρα θετικό. Δηλαδή — το δεν ήταν βέλτιστο. Άτοπο.
Λήμμα 2 — οι δύο σπανιότεροι είναι αδέλφια
Πάλι η εικόνα πρώτα. Σκέψου έναν εσωτερικό κόμβο με ένα μόνο παιδί. Τι προσφέρει; Τίποτα χρήσιμο: προσθέτει ένα δυφίο σε κάθε κώδικα από κάτω του, χωρίς να διαχωρίζει τίποτα (ένα δυφίο έχει νόημα μόνο όταν επιλέγει ανάμεσα σε δύο δρόμους). Αν «συνθλίψουμε» εκείνη την ακμή — σβήσουμε τον άχρηστο κόμβο και ανεβάσουμε το παιδί του — κάθε κώδικας από κάτω μικραίνει κατά ένα δυφίο, άρα και το κόστος πέφτει.
Άρα σε ένα βέλτιστο δέντρο κάθε εσωτερική κορυφή έχει ακριβώς 2 παιδιά — λέμε ότι το δέντρο είναι πλήρες.
Συνδύασε τώρα τα δύο λήμματα. Από το Λήμμα 1, οι δύο σπανιότεροι χαρακτήρες κάθονται στα βαθύτερα φύλλα. Ένα βαθύτερο φύλλο έχει έναν γονέα — κι εκείνος ο γονέας, αφού το δέντρο είναι πλήρες, έχει δύο παιδιά: δύο φύλλα στο ίδιο, βαθύτερο επίπεδο. Μπορούμε λοιπόν πάντα να υποθέσουμε ότι οι δύο σπανιότεροι είναι αδέλφια.
Θεώρημα — η Huffman είναι βέλτιστη
Τι μας έδωσαν τα λήμματα. Κάποιο βέλτιστο δέντρο έχει τους δύο σπανιότερους χαρακτήρες αδέλφια στο βάθος. Αλλά αυτή είναι ακριβώς η πρώτη κίνηση της Huffman! Άρα η άπληστη πρώτη επιλογή δεν θυσιάζει τίποτα. Μένει να δείξουμε ότι και η υπόλοιπη δουλειά — το ίδιο πρόβλημα με έναν χαρακτήρα λιγότερο — λύνεται κι αυτή σωστά. Αυτό φωνάζει επαγωγή.
Το εργαλείο που τα ενώνει είναι μία ταυτότητα. Παίξε με την απόδειξη βήμα-βήμα — πρώτα η ταυτότητα στο πραγματικό δέντρο, μετά η επαγωγή:
Συρρίκνωσε τους δύο σπανιότερους σε έναν σύνθετο χαρακτήρα ω και δες το κόστος να πέφτει ακριβώς κατά f(ω).
Απόδειξη (επαγωγή στο πλήθος χαρακτήρων ).
Βάση (): το δέντρο με μία ρίζα και δύο φύλλα είναι πάντα βέλτιστο — δύο χαρακτήρες, ένα δυφίο ο καθένας, τίποτα να βελτιώσεις.
Επαγωγική υπόθεση: το θεώρημα ισχύει για χαρακτήρες.
Επαγωγικό βήμα. Έστω, για άτοπο, ότι το δέντρο του αλγορίθμου για χαρακτήρες δεν είναι βέλτιστο — υπάρχει δέντρο με . Από το Λήμμα 2, μπορούμε να το πάρουμε με τους δύο σπανιότερους χαρακτήρες αδέλφια.
Από το φτιάχνουμε το : αφαιρούμε τα φύλλα και βάζουμε στη θέση του κοινού γονέα τους έναν σύνθετο χαρακτήρα με . Όμοια, από το φτιάχνουμε το . Κρίσιμη ταυτότητα:
— όταν «βγάζεις» τα δύο αδέλφια, χάνεις ακριβώς από το κόστος, αφού καθένα τους καθόταν ένα επίπεδο βαθύτερα από το . Αφαιρώντας το ίδιο και από τις δύο πλευρές της :
Αλλά το είναι ακριβώς το δέντρο που παράγει ο αλγόριθμος για χαρακτήρες — και από την επαγωγική υπόθεση είναι βέλτιστο. Δεν μπορεί να υπάρχει φθηνότερο. Άτοπο.
Κλείδωσε τη γνώση
Βάλε στη σειρά τα βήματα του αλγορίθμου Huffman:
Και συμπλήρωσε τα κενά:
Μοτίβο σκέψης
Τι μάθαμε
Τι κρατάμε από αυτή τη διάλεξη
- Κωδικοποίηση Huffman — συμπίεση δεδομένων: λίγα δυφία στους συχνούς χαρακτήρες, πολλά στους σπάνιους. Το κέρδος είναι η ανταμοιβή της λοξότητας στις συχνότητες.
- Απροθεματικός κώδικας — κανένας κώδικας δεν είναι πρόθεμα κάποιου άλλου· εγγυάται μονοσήμαντη αποκωδικοποίηση.
- Κάθε απροθεματικός κώδικας = δυαδικό δέντρο με χαρακτήρες στα φύλλα· κόστος .
- Άπληστος αλγόριθμος: συγχώνευσε επανειλημμένα τα δύο σπανιότερα δέντρα. με ουρά προτεραιότητας.
- Ορθότητα μέσω επιχειρήματος ανταλλαγής φύλλων + επαγωγής: η άπληστη πρώτη κίνηση συμφωνεί με κάποιο βέλτιστο δέντρο, και η ταυτότητα ανάγει το πρόβλημα σε μικρότερο.
Ασκήσεις από εξετάσεις
Από τη θεωρία στην εξεταστική
4 άσκησεις που χρησιμοποιούν ως τελευταίο εργαλείο αυτή τη διάλεξη. Οι πρόσφατες εξεταστικές (2024/2025) φέρουν badge προτεραιότητας.
Ιούνιος 2024 · Θέμα 3 — Τέλειο ταίριασμα σε δέντρο
Ένα τέλειο ταίριασμα σε ένα γράφημα είναι ένα σύνολο ακμών έτσι ώστε κάθε κορυφή να περιέχεται σε ακριβώς μία ακμή. Περίγραψε σε φυσική γλώσσα έναν άπληστο αλγόριθμο γραμμικού χρόνου που αποφασίζει αν ένα δέντρο έχει τέλειο ταίριασμα ή όχι, και αιτιολόγησε με 1-2 προτάσεις την ορθότητά του (δηλαδή γιατί επέλεξες αυτό το κριτήριο άπληστης επιλογής).
Φροντιστηριακό Σετ #6 · Άσκηση 7 — Κωδικοποίηση Huffman
Δίνονται οι χαρακτήρες με τις συχνότητές τους: , , , , .
α) Δείξτε τα διαδοχικά βήματα κατασκευής του δένδρου Huffman. β) Δώστε τον πίνακα κωδικοποίησης των χαρακτήρων. γ) Κωδικοποιήστε το «κείμενο» ΚΑΣΤΑΝΑΣ. δ) Αποκωδικοποιήστε το «μήνυμα» (αγνοώντας τυχόν υπόλοιπο).
Φροντιστηριακό Σετ #7 · Άσκηση 1 — Ένωση n ράβδων χρυσού με ελάχιστο κόστος
Δίνονται ράβδοι χρυσού διαφορετικού βάρους. Θέλουμε να τις ενώσουμε σε μία. Το κόστος της ένωσης 2 ράβδων είναι ίσο με το άθροισμα των βαρών τους. Δώστε έναν αποδοτικό άπληστο αλγόριθμο που ελαχιστοποιεί το συνολικό κόστος. (Δεν ζητείται απόδειξη ορθότητας.) Να υπολογιστεί η πολυπλοκότητα.
Φροντιστηριακό Σετ #7 · Άσκηση 12 — Σακίδιο: κλασματικό (άπληστο) vs 0-1
Το 0-1 σακίδιο: έχουμε αντικείμενα, το -οστό με βάρος και αξία , και μια τσάντα που σηκώνει βάρος . Ποια αντικείμενα τοποθετούμε για να μεγιστοποιήσουμε την αξία;
Το κλασματικό σακίδιο: παραλλαγή όπου μπορούμε να πάρουμε ένα κλάσμα ενός αντικειμένου. Λύστε το με άπληστο αλγόριθμο, αποδείξτε την ορθότητά του, και συζητήστε αν υπάρχει άπληστος αλγόριθμος για το 0-1 σακίδιο.