L14 · Δυναμικός Προγραμματισμός I — Memoization & Σταθμισμένος Χρονοπρογραμματισμός
Τι θα δούμε
Ξεκινά το τελευταίο μεγάλο κεφάλαιο: ο δυναμικός προγραμματισμός (dynamic programming, DP). Είναι το πιο εξεταζόμενο κομμάτι της ύλης — και αυτό που οι φοιτητές βρίσκουν πιο «μαγικό». Στόχος αυτής της διάλεξης: να πάψει να είναι μαγικό.
Θα δούμε την ιδέα μέσα από το απλούστερο παράδειγμα (Fibonacci), μετά το πρώτο «πραγματικό» πρόβλημα (σταθμισμένος χρονοπρογραμματισμός διαστημάτων), και θα κωδικοποιήσουμε μια συνταγή 4 βημάτων που εφαρμόζεται σε κάθε DP πρόβλημα.
Τα τρία αλγοριθμικά μοντέλα
Το παράδειγμα-εκκίνηση: Fibonacci
Η ακολουθία Fibonacci ορίζεται αναδρομικά:
Η απευθείας μεταφορά σε κώδικα είναι κομψή — και καταστροφική:
Γιατί είναι τόσο αργό; Επικάλυψη υποπροβλημάτων
Ο κώδικας είναι σωστός — γιατί όμως είναι τόσο αργός; Αντί να το συμπεράνουμε, ας παρακολουθήσουμε τι κάνει στ' αλήθεια η fib. Κάθε κλήση fib(k) γεννά δύο νέες, τις fib(k−1) και fib(k−2)· αυτές γεννούν άλλες δύο η καθεμιά, και ούτω καθεξής. Όλες μαζί σχηματίζουν ένα δέντρο αναδρομής — κι εκεί κρύβεται το πρόβλημα.
Σύρε το n και δες το δέντρο κλήσεων. Πάτησε έναν κόμβο για να φωτιστεί κάθε άλλη κλήση του ίδιου fib(k).
Για fib(5) το memoization κάνει 1.7× λιγότερες κλήσεις.
| fib(n) | Αφελής | Memoization |
|---|---|---|
| fib(10) | 177 | 19 |
| fib(20) | 21.891 | 39 |
| fib(30) | 2.692.537 | 59 |
Αφελής: εκθετική, ≈ 2ⁿ. Memoization: γραμμική, 2n−1. Στο fib(30) η διαφορά είναι ήδη εκατομμύρια προς δεκάδες.
Σύρε το n (κράτησέ το για τώρα στο «Αφελής») και πρόσεξε δύο πράγματα:
- Το δέντρο σχεδόν διπλασιάζεται σε κάθε επιπλέον επίπεδο. Ο μετρητής κλήσεων δεν ανεβαίνει γραμμικά — εκτοξεύεται, περίπου σαν . Ο πίνακας στο τέλος δείχνει πού φτάνει αυτό: το
fib(30)θέλει πάνω από δύο εκατομμύρια κλήσεις. - Πάτησε σε έναν κόμβο — π.χ. σε ένα
fib(2). Φωτίζονται όλες οι άλλες κλήσεις του ίδιουfib(2)στο δέντρο. Είναι πολλές· και η καθεμιά ξαναϋπολογίζει από την αρχή ακριβώς την ίδια τιμή.
Αυτό το δεύτερο είναι η ρίζα του κακού, και έχει όνομα: επικάλυψη υποπροβλημάτων (overlapping subproblems). Το ίδιο υποπρόβλημα — «πόσο κάνει το fib(2);» — ξαναεμφανίζεται αμέτρητες φορές μέσα στην αναδρομή. Ο αφελής αλγόριθμος δεν είναι αργός επειδή έχει δύσκολη δουλειά· είναι αργός επειδή κάνει την ίδια εύκολη δουλειά ξανά και ξανά.
Η λύση: memoization
Αφού το πρόβλημα είναι ότι υπολογίζουμε το ίδιο πράγμα πολλές φορές, η λύση γράφεται σχεδόν μόνη της: υπολόγισέ το μία φορά, και κράτησέ το.
Γύρνα τώρα στο interactive παραπάνω και πάτα το κουμπί «Memoized». Ίδιο n, ίδιο πρόβλημα — αλλά το δέντρο καταρρέει: κάθε fib(k) υπολογίζεται μία μόνο φορά, και κάθε επόμενη φορά που ζητείται επιστρέφεται έτοιμο από τον πίνακα (ο πράσινος κόμβος «cache hit», χωρίς υποδέντρο από κάτω). Ο μετρητής κλήσεων πέφτει από εκθετικός σε γραμμικό.
Στην πράξη το ίδιο αποτέλεσμα το πετυχαίνουμε ακόμα πιο καθαρά — χωρίς καθόλου αναδρομή. Γεμίζουμε έναν πίνακα από κάτω προς τα πάνω (bottom-up): πρώτα τις βασικές περιπτώσεις, και μετά κάθε επόμενο όρο από τους δύο προηγούμενους.
Κάθε υπολογίζεται ακριβώς μία φορά. Η πολυπλοκότητα πέφτει από εκθετική σε γραμμική — αλλάζοντας μόνο πού αποθηκεύεις τα ενδιάμεσα αποτελέσματα.
Σταθμισμένος χρονοπρογραμματισμός διαστημάτων
Τώρα το πρώτο πραγματικό πρόβλημα. Είναι το πρόβλημα του L11 — με μία προσθήκη.
Είσοδος: αιτήματα. Το αίτημα ξεκινά τη στιγμή , τελειώνει τη στιγμή , και έχει βαρύτητα . Δύο αιτήματα είναι συμβατά αν δεν επικαλύπτονται.
Στόχος: υποσύνολο ανά δύο συμβατών αιτημάτων με μέγιστο άθροισμα βαρυτήτων .
Γιατί δεν αρκεί ο άπληστος
Στο L11, όταν όλες οι βαρύτητες ήταν ίσες με , το άπληστο κριτήριο «μικρότερος χρόνος λήξης» έδινε αποδεδειγμένα βέλτιστη λύση. Το ένστικτο λέει πως κάποιο άπληστο κριτήριο θα δουλεύει και τώρα που οι εργασίες έχουν βαρύτητες. Δοκίμασε τα δύο πιο φυσικά — και δες τα να αποτυγχάνουν:
Ο άπληστος εξετάζει τα διαστήματα κατά αύξοντα χρόνο λήξης. Κάθε ράβδος δείχνει την αξία της — δες ποια διαλέγει.
Στο πρώτο στιγμιότυπο, μια φθηνή εργασία που τυχαίνει να τελειώνει πρώτη μπλοκάρει μια εργασία που αξίζει . Στο δεύτερο, μια ακριβή εργασία μπλοκάρει δύο φθηνότερες που μαζί την ξεπερνούν. Το πρόβλημα είναι βαθύ: η σωστή απόφαση για μια εργασία εξαρτάται από όλες τις υπόλοιπες, ενώ ο άπληστος κοιτάζει μόνο μία τη φορά.
Η προετοιμασία: ταξινόμηση και
Πριν στήσουμε την αναδρομή, χρειαζόμαστε δύο προετοιμασίες.
Πρώτη: ταξινόμηση. Διατάσσουμε τα αιτήματα κατά χρόνο λήξης, ώστε . Από εδώ και πέρα, «αίτημα » σημαίνει «το -οστό αίτημα που τελειώνει».
Δεύτερη: η ποσότητα . Φαντάσου ότι αποφασίζεις να βάλεις το αίτημα στη λύση σου. Αμέσως αποκλείεις κάθε προηγούμενο αίτημα που επικαλύπτεται μαζί του. Το ερώτημα είναι: ποιο είναι το τελευταίο προηγούμενο αίτημα που σου μένει ακόμα διαθέσιμο; Αυτό ακριβώς ονομάζουμε .
Ένα χρήσιμο γεγονός: επειδή τα αιτήματα είναι ταξινομημένα κατά λήξη, τα συμβατά με το είναι πάντα ένα αρχικό κομμάτι — κι όλα τα ενδιάμεσα το επικαλύπτουν. Διάλεξε ένα αίτημα παρακάτω και δες ποιο είναι το του και ποια αιτήματα αποκλείει:
Πάτησε σε ένα αίτημα. Κίτρινο = το j · μπλε = το p(j) · κόκκινο = ασύμβατα που αποκλείονται.
Η αναδρομή — μια δυαδική επιλογή
Ορίζουμε = η τιμή της βέλτιστης λύσης για τα αιτήματα . Κοιτάμε το αίτημα και ρωτάμε: μέσα ή έξω;
- Το είναι ΜΕΣΑ. Τότε αποκλείονται όλα τα ασύμβατα με το — τα . Ό,τι μένει είναι η βέλτιστη λύση για τα . Συνεισφορά: .
- Το είναι ΕΞΩ. Τότε η βέλτιστη λύση είναι ακριβώς η βέλτιστη για τα . Συνεισφορά: .
Δεν ξέρουμε ποια περίπτωση ισχύει — οπότε παίρνουμε τη μεγαλύτερη:
Δες την αναδρομή να γεμίζει τον πίνακα. Σε κάθε βήμα φαίνονται οι δύο υποψήφιες τιμές — «το μέσα» και «το έξω» — και ποια κερδίζει:
Κίτρινο = το αίτημα j · μπλε = ο συμβατός προκάτοχος p(j) · πράσινο = στη λύση.
Ο αλγόριθμος
Η απευθείας αναδρομή είναι εκθετική — ακριβώς όπως ο αφελής Fibonacci, το πλήθος κλήσεων μεγαλώνει σαν . Η θεραπεία είναι η ίδια: απομνημόνευση.
- Είσοδος:
- n αιτήματα με χρόνους έναρξης/λήξης και βαρύτητες
- Έξοδος:
- η μέγιστη συνολική βαρύτητα ενός συνόλου συμβατών αιτημάτων
Με λόγια. Ταξινόμησε τα αιτήματα κατά χρόνο λήξης και υπολόγισε όλα τα . Μετά γέμισε έναν πίνακα με αύξουσα σειρά: το είναι το μεγαλύτερο ανάμεσα στο «πάρε το » () και στο «παράτα το » (). Το είναι η απάντηση.
Ισοδύναμα, top-down με memoization — η αναδρομή λύνει κάθε υποπρόβλημα, αλλά το αποθηκεύει:
Υπολογισμός των σε
Είπαμε πως όλα τα βγαίνουν σε . Δεν είναι προφανές: ο απλός τρόπος — για κάθε , ψάξε προς τα πίσω για το τελευταίο συμβατό αίτημα — κάνει δουλειά. Πώς πέφτουμε στο ;
Το κόλπο: έχουμε δύο ταξινομημένες ακολουθίες — τους χρόνους λήξης και τους χρόνους έναρξης — και τις συγχωνεύουμε, ακριβώς όπως το merge της συγχωνευτικής ταξινόμησης (L03). Δύο δείκτες, ο πάνω στις λήξεις και ο πάνω στις ενάρξεις, σαρώνουν τις ακολουθίες από μία φορά:
- Αν η τρέχουσα λήξη είναι από την τρέχουσα έναρξη , τότε το αίτημα προλαβαίνει να τελειώσει — είναι έγκυρος προκάτοχος. Προχωράμε τον .
- Αλλιώς, η λήξη έρχεται πολύ αργά. Άρα ο τελευταίος που πρόλαβε ήταν ο : θέτουμε και προχωράμε τον .
Κάθε βήμα προχωρά έναν από τους δύο δείκτες κατά ένα, και κανένας δεν ξεπερνά το — άρα το πολύ βήματα συνολικά. Δες τη σάρωση να τρέχει βήμα-βήμα — στο interactive κάθε έναρξη δείχνει σε ποιο αίτημα ανήκει, ώστε το να καταγράφεται στο σωστό:
Συγχώνευση δύο ταξινομημένων πινάκων με δύο δείκτες — όπως το merge της συγχωνευτικής ταξινόμησης.
Εύρεση της λύσης
Ο αλγόριθμος υπολογίζει τη μέγιστη τιμή. Για να βρούμε ποια αιτήματα την πετυχαίνουν, κάνουμε ένα πέρασμα προς τα πίσω — σε κάθε ρωτάμε «ποια από τις δύο περιπτώσεις κέρδισε;»
Η συνταγή του δυναμικού προγραμματισμού
Κάθε πρόβλημα DP — όσα θα δούμε στα L15–L17 — λύνεται με τα ίδια 4 βήματα:
Κλείδωσε τη γνώση
Βάλε στη σειρά τα βήματα του DP για τον σταθμισμένο χρονοπρογραμματισμό:
Και συμπλήρωσε τα κενά:
Μοτίβο σκέψης
Τι μάθαμε
Τι κρατάμε από αυτή τη διάλεξη
- Δυναμικός προγραμματισμός — διάσπαση σε επικαλυπτόμενα υποπροβλήματα· λύσε καθένα μία φορά, αποθήκευσέ το.
- Fibonacci — αφελής αναδρομή λόγω επικάλυψης· memoization → .
- Σταθμισμένος χρονοπρογραμματισμός — μέγιστη βαρύτητα συμβατών αιτημάτων. Ο άπληστος αποτυγχάνει.
- = το τελευταίο συμβατό προηγούμενο αίτημα. Αναδρομή: .
- Χρόνος — η ταξινόμηση κυριαρχεί· τα βγαίνουν σε με σάρωση δύο δεικτών, και η εύρεση της λύσης σε με πέρασμα προς τα πίσω.
- Η συνταγή DP (4 βήματα): δομή → αναδρομικός ορισμός → υπολογισμός τιμής → κατασκευή λύσης.
Ασκήσεις από εξετάσεις
Από τη θεωρία στην εξεταστική
10 άσκησεις που χρησιμοποιούν ως τελευταίο εργαλείο αυτή τη διάλεξη. Οι πρόσφατες εξεταστικές (2024/2025) φέρουν badge προτεραιότητας.
Ιούνιος 2025 · Θέμα 1.7 — Πολυπλοκότητα δισδιάστατου πίνακα DP
Λύνουμε ένα πρόβλημα με δυναμικό προγραμματισμό συμπληρώνοντας έναν πίνακα με τιμές , για και . Ποια από τα παρακάτω μπορούμε να πούμε με βεβαιότητα ότι δεν αντικατοπτρίζει τη χρονική πολυπλοκότητα;
(i) · (ii) · (iii) · (iv) .
Ιούνιος 2025 · Θέμα 1.8 — Πολυπλοκότητα μονοδιάστατου πίνακα DP
Όμοια, λύνουμε ένα πρόβλημα με DP συμπληρώνοντας έναν πίνακα τιμών για . Ποια μπορούμε να πούμε με βεβαιότητα ότι δεν αντικατοπτρίζει τη χρονική πολυπλοκότητα;
(i) · (ii) · (iii) · (iv) .
Ιούνιος 2025 · Θέμα 3 — Επίσκεψη αξιοθέατων (DP)
Θέλουμε να επισκεφτούμε μία ακολουθία από αξιοθέατα σε μία πόλη. Οι μόνες επιλογές μετακίνησης είναι ταξί ή ηλεκτρικό πατίνι, του οποίου η μίσθωση ισχύει για 4 διαδρομές. Με ταξί, η μετάβαση από το στο κοστίζει (η μετάβαση στο πρώτο αξιοθέατο είναι δωρεάν). Η ενοικίαση πατινιού κοστίζει σταθερά . Ορίζουμε = το ελάχιστο κόστος για να επισκεφθούμε τα .
(i) Ποια τιμή δίνει το ελάχιστο συνολικό κόστος; (ii) Όρισε αναδρομικά το . (iii) Ποια είναι η χρονική πολυπλοκότητα και γιατί;
Σεπτέμβριος 2025 · Θέμα 1.6 — Πολυπλοκότητα δισδιάστατου πίνακα DP
Λύνουμε ένα πρόβλημα με DP συμπληρώνοντας έναν πίνακα τιμών , για , . Ποιες επιλογές μπορούμε να πούμε με βεβαιότητα ότι δεν αντικατοπτρίζουν τη χρονική πολυπλοκότητα;
(i) · (ii) · (iii) · (iv) .
Σεπτέμβριος 2025 · Θέμα 1.7 — Πολυπλοκότητα μονοδιάστατου πίνακα DP
Λύνουμε ένα πρόβλημα με DP συμπληρώνοντας έναν πίνακα τιμών για . Ποιες μπορούμε να πούμε με βεβαιότητα ότι δεν αντικατοπτρίζουν τη χρονική πολυπλοκότητα; (i) · (ii) · (iii) · (iv) .
Φροντιστηριακό Σετ #8 · Άσκηση 3 — Τεμαχισμός ράβδου (rod cutting)
Το πρόβλημα τεμαχισμού μιας ράβδου: δίνεται μια ράβδος μήκους cm, και το κέρδος πώλησης για κάθε δυνατό μήκος τμήματος ( για τμήμα μήκους ). Δώσε αλγόριθμο που βρίσκει τον πιο επικερδή τρόπο τεμαχισμού.
Φροντιστηριακό Σετ #8 · Άσκηση 4 — Άνοιγμα εστιατορίων κατά μήκος δρόμου
Σκέφτεστε να ανοίξετε μια σειρά εστιατορίων κατά μήκος ενός αυτοκινητόδρομου. Οι πιθανές τοποθεσίες σχηματίζουν ευθεία γραμμή, με αποστάσεις από την αρχή (σε χιλιόμετρα, κατά αύξουσα σειρά) . Σε κάθε τοποθεσία μπορείτε να ανοίξετε το πολύ ένα εστιατόριο· το προσδοκώμενο κέρδος από το άνοιγμα στην τοποθεσία είναι . Δύο οποιαδήποτε εστιατόρια πρέπει να απέχουν τουλάχιστον χιλιόμετρα. Δώστε αποδοτικό αλγόριθμο για τον υπολογισμό του μέγιστου συνολικού κέρδους.
Ιούνιος 2023 · Θέμα 4 — Κολώνες φωτισμού (μέγιστο ανεξάρτητο σύνολο σε μονοπάτι)
Ο δήμος θέλει να εγκαταστήσει κολώνες φωτισμού σε πιθανές θέσεις κατά μήκος ενός δρόμου. Για εξοικονόμηση κόστους δεν τοποθετεί κολώνες σε δύο διαδοχικές θέσεις. Κάθε θέση έχει φωτεινότητα · στόχος είναι ένα υποσύνολο μη-διαδοχικών θέσεων με τη μέγιστη συνολική φωτεινότητα («μέγιστο ανεξάρτητο υποσύνολο»).
Παράδειγμα 7 θέσεων με φωτεινότητες (για ). Π.χ. τα ανεξάρτητα έχουν φωτεινότητες .
1. Ο εξής άπληστος αλγόριθμος επιλέγει το καλύτερο ανάμεσα στο σύνολο των κορυφών με περιττούς δείκτες και σε αυτό με άρτιους δείκτες. Είναι βέλτιστος; Αν όχι, δώσε αντιπαράδειγμα. 2. Σχεδίασε αλγόριθμο δυναμικού προγραμματισμού που βρίσκει τη μέγιστη συνολική φωτεινότητα (δώσε την αναδρομική σχέση). 3. Δώσε τον χρόνο εκτέλεσης — πρέπει να είναι πολυωνυμικός ως προς το και ανεξάρτητος των τιμών φωτεινότητας. 4. Εκτέλεσε τον αλγόριθμο στο παραπάνω παράδειγμα.
Σεπτέμβριος 2023 · Θέμα 2 — Χρονοπρογραμματισμός με βάρη (πλατφόρμα δόνησης)
Το γυμναστήριο της γειτονιάς σας απέκτησε πρόσφατα μια υπερσύγχρονη πλατφόρμα δόνησης, ένα πολύ ακριβό όργανο που υπόσχεται μυϊκή ενδυνάμωση. Πολλοί αθλούμενοι θέλουν να τη χρησιμοποιήσουν: κάθε αίτημα χαρακτηρίζεται από έναν χρόνο έναρξης , έναν χρόνο λήξης και μια συνδρομή που είναι διατεθειμένος να πληρώσει. Υπάρχει μόνο μία πλατφόρμα, οπότε δεν μπορούν να εξυπηρετηθούν δύο αιτήματα που επικαλύπτονται χρονικά. Το γυμναστήριο θέλει να επιλέξει ένα υποσύνολο μη επικαλυπτόμενων αιτημάτων ώστε να μεγιστοποιηθεί το συνολικό άθροισμα των συνδρομών.
(Α) Θεωρήστε τον εξής άπληστο αλγόριθμο: ταξινόμησε τα αιτήματα κατά φθίνουσα συνδρομή, διάλεξε το πρώτο, και κατόπιν, σαρώνοντας τη λίστα, διάλεξε κάθε επόμενο αίτημα που είναι συμβατό (δεν επικαλύπτεται) με όσα έχεις ήδη επιλέξει. Επιλύει ο αλγόριθμος αυτός το παραπάνω πρόβλημα; Αν όχι, δώστε αντιπαράδειγμα.
(Β) Βρείτε την τιμή (συνολικό άθροισμα των συνδρομών) της βέλτιστης λύσης.
Σημείωση μεταγραφής: το πρωτότυπο είναι αχνό σκαναρισμένο φύλλο με αιτήματα. Παρακάτω διδάσκουμε πλήρως τη μέθοδο και τη δουλεύουμε σε ένα καθαρό, αντιπροσωπευτικό στιγμιότυπο.
Ιούνιος 2022 · Θέμα 2 — Αναδρομή vs δυναμικός προγραμματισμός
Θέλουμε να υπολογιστεί η ακολουθία που προκύπτει από τον αναδρομικό τύπο
με τους 3 αρχικούς όρους . Έστω ο αλγόριθμος που στηρίζεται απευθείας στην αναδρομική σχέση. (Δίνεται ότι .)
i. Γράψτε σε φυσική γλώσσα τον αλγόριθμο . ii. Δείξτε ότι ο είναι εκθετικός, με πολυπλοκότητα . iii. Αν χρησιμοποιήσουμε δυναμικό προγραμματισμό (αλγόριθμος ), πόσα υποπροβλήματα θα οριστούν; iv. Δικαιολογήστε ότι η πολυπλοκότητα του είναι γραμμική. v. Ποιος αλγόριθμος είναι ταχύτερος;