(13 ο ) ΔΥΝΑΜΙΚΟΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ ΙII: «βέλτιστο στατικό ευρετήριο» Βέλτιστο στατικό «μεροληπτικό» ευρετήριο «Ευρετήρια» ονομάζουμε δομές οι οποίες μας διευκολύνουν να εντοπίζουμε τα καταχωρισμένα στοιχεία μέσω της «ταυτότητας» που έχουν αυτά. Σκεφθείτε λ.χ. ένα τηλεφωνικό ευρετήριο, ή ένα ευρετήριο ενός βιβλίου, ή ένα ευρετήριο προϊόντων. Συχνότατα αυτά τα ευρετήρια είναι στατικά ή σχεδόν στατικά. Π.χ. το ευρετήριο μιας ψηφιακής εγκυκλοπαίδειας που διανέμεται σε DVD, είναι στατικό (όσο διαρκεί η συγκεκριμμένη έκδοσή της). Για την παράσταση των ευρετηρίων υπάρχουν πολλοί τρόποι, ένας εκ των οποίων είναι τα (δυαδικά) δένδρα διχοτομικής αναζήτησης γνωστά από κάθε μάθημα δομών δεδομένων: τοποθετούμε τα στοιχεία σε ένα δυαδικό δένδρο, με «αριστερούς» και «δεξιούς» θυγατρικούς έτσι ώστε όλα τα στοιχεία αριστερά ενός κόμβου να είναι μικρότερα από αυτό, και όλα δεξιά από αυτόν να είναι μεγαλύτερα. Έτσι η αναζήτηση ενός οποιουδήποτε στοιχείου οδεύει κατά μήκος ενός κλάδου, και είμαστε σε θέση να μικρύνουμε το κόστος αυτής της αναζήτησης κρατώντας βραχείς τους κλάδους του δένδρου. Π.χ. στο παρακάτω δένδρο με περιεχόμενο αριθμούς, η αναζήτηση του σ = 43 οδεύει κατά μήκους του «μαύρου» κλάδου: 31 18 55 12 25 48 88 23 38 43 βάθος(43) = 5 Αν ένα στοιχείο σ τοποθετηθεί σε βάθος β(σ), δηλαδή απέχει β(σ) κόμβους από την ρίζα, τότε το κόστος εύρεσης αυτού του στοιχείου είναι προφανώς Θ(β(σ)). Υπάρχει όμως περίπτωση να μην τυχαίνει να αναζητούμε όλα τα στοιχεία με την ίδια πιθανότητα, (όπως αυτή θα μπορούσε να προκύψει λχ. από την συχνότητα αναζήτησης), αλλά κάθε στοιχείο σ με πιθανότητα pσ. (Εξ αυτού ο όρος «μεροληπτικό» ευρετήριο: ευνοεί την αναζήτηση των πιο «πιθανών» στοιχείων.) Τότε, θα έπρεπε να σταθμίσουμε το κόστος εύρεσης β(σ) πολλαπλασιάζοντας αυτό επί την πιθανότητα αναζήτησης, pσ. Το αναμενόμενο (ή μέσο) κόστος αναζήτησης θα δίδεται λοιπόν από το άθροισμα L = βσ ( )p σ, που μας δίδει το «αναμενόμενο» βάθος ενός στοιχείο. Το καλύτερο δενδρικό ευρετήριο θα ήταν εκείνο που ελαχιστοποιεί το παραπάνω «αναμενόμενο» βάθος. Ποιό όμως είναι αυτό το δένδρο πώς, δηλαδή, να το κατασκευάσουμε; σ ΔΙΔΕΤΑΙ: «ΒΕΛΤΙΣΤΟ ΣΤΑΤΙΚΟ (ΔΕΝΔΡΙΚΟ) ΕΥΡΕΤΗΡΙΟ» Ν στοιχεία, σk, k = 1,..., (α) μέλη μιας διάταξης, (β) και έκαστο με μια «πιθανότητα αναζήτησης» 0 < pk < 1, 1 1 p k = k = ΖΗΤΕΙΤΑΙ: Ένα δένδρο διχοτομικής αναζήτησης ΩΣΤΕ:...με το μικρότερο αναμενόμενο κόστος αναζήτησης L(T) = βάθος( k) p k = 1 k. Πανεπιστήμιο Κρήτης Τμήμα Επιστήμης Υπολογιστών Γ.Φ. Γεωργακόπουλος ver: 28/3/2014 1
Η απλοϊκή λύση θα ήταν βέβαια να εξετάσουμε εξαντλητικά όλα τα δένδρα διχοτομικής αναζήτησης με Ν κόμβους, αυτά όμως είναι αστρονομικού πλήθους. Στα μαθήματα «διακριτών μαθηματικών», αυτό το πλήθος υπολογίζεται σε 1 2n Cn =, n + 1 n έκφραση που έχει αποκτήσει όνομα λέγονται «οι Καταλανικοί αριθμοί». Η τιμή της κατά προσέγγιση είναι 4 n /(n+1) έχει δηλαδή μια ασυμπτωτική συμπεριφορά σαφώς εκθετική... Μια άλλη απλή ιδέα που ίσως να πέρασε από το νού σας είναι ότι το καλύτερο δένδρο προκύπτει βάζοντας κάθε φορά το πιθανότερο στοιχείο στη ρίζα. Αυτό όμως δεν είναι αλήθεια: στο παρακάτω σχήμα έχουμε 7 στοιχεία, και δίπλα στο καθένα έχουμε ένα βάρος, που δίδει «%» την πιθανότητα αναζήτησής του. (Για το «Γ», π.χ., pγ = 12% = 0.12.) L = 2.44 L = 2.47 L = 2.42 Ζ, 25 Ε, 15 Ε, 15 Η, 23 Δ, 8 Γ, 12 Η, 23 Γ, 12 Β, 7 Ζ, 25 Α, 10 Δ, 8 Ζ, 25 Α, 10 Δ, 8 Α, 10 Γ, 12 Ε, 15 Η, 23 Β, 7 Β, 7 "Το πιθανότερο στη ρίζα" "Ισορροπημένο" Καλύτερο και από τα δύο... Το 1 ο δένδρο έχει κατασκευαστεί βάζοντας στη εκάστοτε ρίζα το εκάστοτε πιθανότερο στοιχείο και έχει μέσο βάθος 2.44. Το 2 ο είναι το ισορροπημένο δένδρο, με μέσο βάθος, 2.47 όχι καλύτερο. Το 3 ο δένδρο όμως είναι καλύτερο και από τα δύο, με μέσο βάθος 2.42 (και δεν είναι το βέλτιστο...). Πώς θα βρούμε λοιπόν το βέλτιστο δενδρικό ευρετήριο; Μια προσπάθεια αναγωγής σε «υποπροβλήματα»: Για να δούμε το πώς μπορούμε να αναγάγουμε την λύση του προβλήματος μας σε μικρότερα, είναι να εξετάσουμε το πώς εξαρτάται το κόστος που μας ενδιαφέρει (το αναμενόμενο βάθος), από την δομή του δένδρου αναζήτησης. Αυτό αποτελείται (εξ ορισμού για τα δένδρα) από την ρίζα και τα δύο υπόδενδρα, το αριστερό ΤΑ και το δεξιό ΤΔ. Στη ρίζα, προφανώς δεν πρόκειται να βρεθεί παρά ένα από τα στοιχεία, k = 1,...,. Έστω ότι για το δένδρο Τ (k) διαλέγουμε ως ρίζα το k στό στοιχείο. H 1 η καίρια παρατήρηση εδώ είναι ότι, αφού το δένδρο μας είναι δένδρο διχοτομικής αναζήτησης θα πρέπει όλα τα μικρότερα από το σk στοιχεία σ1,..., σk 1 να τεθούν στο αριστερό υπόδενδρο, και όλα τα μεγαλύτερα, σk+1,..., σ. στο δεξιό υπόδενδρο. Η 2 η καίρια παρατήρηση είναι ότι το βάθος β () Τ i του σi στοιχείου είτε στο αριστερό υπόδενδρο Α (είτε το βάθος του β () Τ i στο δεξιό), είναι ακριβως κατά 1 μονάδα μικρότερο από το βάθος του στο όλο Δ δένδρο Τ (k) : και στις δύο περιπτώσεις για να φτάσουμε έως το σi αποφεύγουμε μιαν ακμή, αυτήν που συνδέει την ρίζα με το αριστερό ή δεξιό υπόδενδρο. Το κόστος αναζήτησης, επομένως, γράφεται ω εξής: Πανεπιστήμιο Κρήτης Τμήμα Επιστήμης Υπολογιστών Γ.Φ. Γεωργακόπουλος ver: 28/3/2014 2
( k ) k 1 ( ) = ( ) 1 k = ( k )( ) k + ( ( ) 1) ( ( ) 1) 1 T + k i A i + = T = i= k+ 1 T + Δ i LT β k p β k p β i p β i p = p + β () i p + β () i p k 1 k= 1 k i= 1 TA i i= k+ 1 TΔ i = p + L( T ) + L( T ) k = 1 k Α Δ Από την παραπάνω σχέση καθίσταται προφανές ότι το καλύτερο που μπορούμε να κάνουμε είναι να διαλέξουμε ως ριζικό στοιχείο k εκείνο που καθιστά την παραπάνω έκφραση ελάχιστη. Βέβαια αυτό απαιτεί τον υπολογισμού του βέλτιστου στατικού δένδρου ΤΑ για τα στοιχεία σ1,..., σk 1, και του βέλτιστου στατικού δένδρου ΤΔ για τα στοιχεία σk+1,..., σ. Αυτά όμως είναι «υπο προβλήματα» του αρχικού προβλήματος, διότι αφορούν μέρος μόνο των αρχικών δεδομένων. Μπορούμε να έχουμε λοιπόν μια αναδρομική λύση: Αλγόριθμος «Στατικό ευρετήριο βέλτιστο κόστος» (δοκιμαστική εκδοχή) ΜέσοΒάθος( Ρ: πίνακας Ν πιθανοτήτων, i,j: θέσεις-του-πίνακα) { L Ν Περίπτωση { (i>j): L 0 (i=j): L Ρ(i) // = P(j) (i<j): Π Σ k=i..j P[k] Για k = i έως j L min( L, Π + ΜέσοΒάθος(Ρ,i,k-1) + ΜέσοΒάθος(Ρ,k+1,j)) } ΜέσοΒάθος L } Προσέξτε στον παραπάνω κώδικα, άλλο ένα χαρακτηριστικό του «δυναμικού προγραμματισμού»: το διδόμενο πρόβλημα που εδώ αφορά στα στοιχεία i έως j, «ανάγεται» σε πολλά υποπροβλήματα του ιδίου τύπου ΜέσοΒάθος(...), συγκεκριμμένα σε (j i+1) τέτοια προβλήματα. (Βλ. την εντολή «για k = i έως j».) Στην τεχνική δ & β αυτά τα υποπροβλήματα ήσαν 2, 3, ή, εν πάσει περιπτώσει, πλήθους Θ(1). Εδώ το πλήθος τους είναι μεταβλητό και μή φραγμένο. Η ανάλυση πλοκής της παραπάνω διαδικασίας δεν είναι απλή, μπορούμε όμως να βρούμε πιο εύκολα ένα κάτω φράγμα Τ(Ν), (για Ν = j i+1 ) των βημάτων που θα χρειαστεί. Η κλήση ΜέσοΒάθος(Ρ,i,j) καλείται να χειριστεί τα Ν στοιχεία από i έως j, χωρίζοντας αυτά σε δύο μέρη, ένα των οποίων θα έχει σίγουρα μέγεθος τουλάχιστον Ν/2. Θα έχουμε λοιπόν Τ(Ν) > Ν Τ(Ν/2). Αν αντικαταστήσουμε την εμφάνιση του Τ(x) στο δεξιό μέλος, (με βάση την ίδια έκφραση), για x = Ν/2, Ν/2 2, κοκ, θα προκύψει η έκφραση: Τ(Ν) > Ν(Ν/2) (Ν/2 2 ) (Ν/2 3 )... T(Ν/2 k ) Σταματάμε στο k log, ώστε 2 k, T(Ν/2 k ) = Τ(1) = Θ(1), και επειδή 2 2 2... 2 k =2 (1+2+3+ +k) = 2 k(k+1)/2 = Ν (k+1)/2, λαμβάνουμε από τους (k+1) όρους το εξής: Τ(Ν) = Ω(Ν k+1 / (k+1)/2 ) = Ω(Ν 0.5(k+1) ). 0.5log Δηλαδή, T( ) =Ω ( ). Αυτό μέγεθος το πλήθος αυξάνει κατά απαγορευτικό τρόπο, διότι έχει μια ασυμπτωτική συμπεριφορά σαφώς εκθετική: ο εκθέτης 0.5log δεν μένει φραγμένος, αλλά τείνει στο άπειρο, (π.χ. για Ν = 1000 θα έχουμε Τ(Ν) >> 10 15 ). «Yποπροβλήματα»; ποιά «υποπροβλήματα»; Πανεπιστήμιο Κρήτης Τμήμα Επιστήμης Υπολογιστών Γ.Φ. Γεωργακόπουλος ver: 28/3/2014 3
Τί συμβαίνει; Η αιτία είναι ότι ο παραπάνω κώδικας υπολογίζει τα ίδια και τα ίδια ξανά... Σε μια κλήση διαλέγοντας ως ρίζα λ.χ. το 21 ο στοιχείο, εξετάζει τι μπορεί να κάνει με τα στοιχεία 1 έως 20. Σε μια άλλη φάση όμως διαλέγοντας το 30 ο στοιχείο θα εξετάσει τι μπορεί να κάνει για τα στοιχεία 1 έως 30, και όταν για ρίζα αυτών των στοιχείων δοκιμάσει το 21 ο στοιχείο, έχοντας ξεχάσει τους προηγούμενους υπολογισμούς, θα προχωρήσει στον υπολογισμό εκ νέου του καλύτερου δένδρου για τα 1 έως 20 στοιχεία, κοκ. Ο παραπάνω κώδικας υπολογίζει την λύση για τα ίδια και τα ίδια υπο προβλήματα πολλές φορές... Ας ξανακυττάξουμε λοιπόν ποιά και εξ αυτού, πόσα υποπροβλήματα ενδέχεται να παίξουν ρόλο εδώ. Χωρίζοντας τα Ν στοιχεία σε δύο μέρη μένουμε με ένα αριστερό δένδρο που περιέχει τα στοιχεία σ1,..., σk 1, αλλά χωρίζοντας και αυτό στη συνέχεια σε δύο μέρη με βάση το στοιχείο σλ, θα μείνουμε με δύο δένδρα με στοιχεία, σ1,..., σλ 1 και σλ+1,..., σk 1. Η 2 η μορφή είναι η γενική μορφή υποπροβλήματος που μας ενδιαφέρει (όταν i = j έχουμε ένα μόνον στοιχείο, οπότε η λύση είναι μία και προφανής, με κόστος L = pi = pj): «ποιό είναι το βέλτιστο δένδρο για τα στοιχεία σi έως σj, 1 i < j» Όλα αυτά όμως τα υποπροβλήματα είναι ενός σχετικά εύλογου πλήθους: δεν είναι παρά Θ(Ν 2 ). Όχι μόνον δεν χρειάζεται λοιπόν να τα επανυπολογίζουμε, αλλά μπορούμε να μην τα επανυπολογίζουμε, κρατώντας την λύση τους σε ένα πίνακα (Ν+1) (Ν+1) (το πολύ) και χρησιμοποιώντας τα μικρότερα από αυτά για να υπολογίζουμε την λύση των μεγαλύτερων. Η σωστή εκδοχή του παραπάνω αλγορίθμου αποθηκεύει (λ.χ. σε έναν πίνακα) όσες λύσεις υπολογίζουμε και χρησιμοποιεί τις αποθηκευμένες λύσεις μικρότερων στιγμιοτύπων για να κατασκευάσει τις λύσεις ολοένα μεγαλύτερων στιγμιοτύπων, έως το αρχικό: Αλγόριθμος «Στατικό ευρετήριο βέλτιστο κόστος» (ορθή εκδοχή) ΒέλτιστοΔένδρο( Ρ: πίνακας Ν πιθανοτήτων) ΜέσοΒάθος: πίνακας [1..Ν+1,0..Ν] { // πρωτοτιμοδότηση // προβλήματα μεγέθους 0 Για i = 1 έως Ν { ΜέσοΒάθος[i,i-1} 0 } Για j = 1 έως Ν { ΜέσοΒάθος[j+i,j} 0 } // προβλήματα μεγέθους 1 Για k = 1 έως Ν { ΜέσοΒάθος[k,k} P[k] 1 } Τ(Ν) // προβλήματα μεγέθους 2, 3,..., Ν Για μέγεθος = 2 έως Ν { Για i = 1 έως Ν-μέγεθος+1 { j = i+μέγεθος-1 ΜέσοΒάθος[i,j] Π Σ k=i..j P[k] Για k = i έως j { L Π + ΜέσοΒάθος[i,k-1] + ΜέσοΒάθος[k+1,j] Εάν L < ΜέσοΒάθος[i,j] τότε { ΜέσοΒάθος[i,j] L // επιλογή βέλτιστου ριζικού στοιχείου } } } } } για μέγεθος, 1 /3Ν.. 2 /3Ν = Ν /3 φορές 1 /3Ν Θ(1) Θ(1) 1 /3Ν 1 ος 2 ος 3 ος Προσέξτε στον παραπάνω κώδικα, ένα ακόμα βασικό χαρακτηριστικό του «δυναμικού προγραμματισμού»: δεν προχωρούμε «αναδρομικά» διακινδυνεύοντας έτσι την ανεξέλεκτη δαπάνη χρόνου σε εκτενείς επανυπολογισμούς της (ίδιας) λύσης σε ίδια στιγμιότυπα αλλά προχωρούμε «επαγωγικά», από την λύση των μικρότερων στιγμιοτύπων στη λύση των μεγαλύτερων. Πανεπιστήμιο Κρήτης Τμήμα Επιστήμης Υπολογιστών Γ.Φ. Γεωργακόπουλος ver: 28/3/2014 4
Η κοστολόγηση του παραπάνω κώδικα θέλει λίγη προσοχή. Το κόστος προέρχεται προφανώς από τους τρείς παρένθετους επαναληπτικούς βρόχος, έκαστος των οποίων επαναλαμβάνεται το πολύ Ν φορές, επομένως το κόστος είναι Ο(Ν 3 ). Είναι όμως αυτή καλή εκτίμηση; Διότι ο μέν 1 ος επαναληπτικός βρόχος θα επαναληφθεί Ν φορές, αλλά όσο η τιμή της μεταβλητής «μέγεθος» αυξάνει, τόσο μειώνεται το πλήθος των επαναλήψεων του 2 ου βρόχου, αν και, ταυτοχρόνως, τόσο αυξάνεται το πλήθος επαναλήψεων του 3 ου βρόχου. Για τις Ν/3 τιμές όμως της «μέγεθος» από 1 /3Ν εώς 2 /3Ν, και οι δύο άλλοι εσώτεροι βρόχοι θα επαναληφθούν τουλάχιστον Ν/3 φορές. Άρα το κόστος είναι μεν Ο(Ν 3 ), αλλά και Ω((Ν/3) 3 ) = Ω(Ν 3 ), δηλαδή Θ(Ν 3 ). Ανάμεσα σε πλοκή = Ν 3 και = Ν 0.5log τί προτιμάμε; Ασυμπτωτικά, προφανώς το 1 ο. Το σημείο «διασταύρωσης» μετά το οποίο η 1 η πλοκή καθίσταται προτιμότερη είναι πολύ μικρό: Ν 2 6 = 64. Και ποιό είναι το βέλτιστο «δένδρο»: Μια τελευταία λεπτομέρεια απομένει ίσως όχι τόσο λεπτομέρεια. Στο δάσος των υποπροβλημάτων χάσαμε το δένδρο της λύσης... : δώσαμε μέχρι στιγμής έναν αλγόριθμο που υπολογίζει μεν το κόστος της βέλτιστης λύσης, αλλά δεν παραδίδει την ίδια τη λύση, το βέλτιστο δένδρο δηλαδή. Για την ακρίβεια όχι μόνο δεν παραδίδει λύση δεν φαίνεται καν πού την υπολογίζει. Βέβαια θα ήταν πολύ παράξενο ένας αλγόριθμος να είναι σε θέση να υπολογίζει το κόστος της βέλτιστης χωρίς να είναι σε θέση να κατασκευάσει αυτή την λύση... Κάπου ανάμεσα στις όχι και τόσο πολλές εντολές του κώδικα που δώσαμε θα πρέπει να παράγονται τα στοιχεία που χρειαζόμαστε για την κατασκευή του βέλτιστου δένδρου. Και αυτό το σημείο είναι εκεί όπου επιλέγονται τα εκάστοτε «ριζικά» στοιχεία, συγκεκριμμένα στην γραμμή «// επιλογή βέλτιστου ριζικού στοιχείου». Εκεί θα έπρεπε να θέσουμε μια εντολή καταχώρισης του στοιχείου που παράγει την εκάστοτε βέλτιστη λύση. Ο κώδικας θα είχε τότε την εξής πλήρη μορφή: Αλγόριθμος «Στατικό ευρετήριο βέλτιστο κόστος» (πλήρης εκδοχή) ΒέλτιστοΚόστος( Ρ: πίνακας Ν πιθανοτήτων) ΜέσοΒάθος: πίνακας [1..Ν+1,0..Ν] { // πρωτοτιμοδότηση // προβλήματα μεγέθους 0 Για i = 1 έως Ν { ΜέσοΒάθος[i,i-1} 0 } Για j = 1 έως Ν { ΜέσοΒάθος[j+i,j} 0 } // προβλήματα μεγέθους 1 Για k = 1 έως Ν { ΜέσοΒάθος[k,k} P[k] } // προβλήματα μεγέθους 2..Ν Για μέγεθος = 2 έως Ν { Για i = 1 έως Ν-μέγεθος+1 { j = i+μέγεθος-1 ΜέσοΒάθος[i,j] Π Σ k=i..j P[k] Για k = i έως j { L Π + ΜέσοΒάθος[i,k-1] + ΜέσοΒάθος[k+1,j] Εάν L < ΜέσοΒάθος[i,j] τότε { ΜέσοΒάθος[i,j] L // επιλογή βέλτιστου ριζικού στοιχείου Ριζα[i,j] k } } } } } Από την παραπάνω εκδοχή μπορούμε να ανακατασκευάσουμε το βέλτιστο δένδρο. Θέλει όμως και αυτό μια μικρή προσπάθεια. Ρίζα του δένδρου θα είναι, ύστερα από όσα έχουμε πεί, ο κόμβος Πανεπιστήμιο Κρήτης Τμήμα Επιστήμης Υπολογιστών Γ.Φ. Γεωργακόπουλος ver: 28/3/2014 5
ρ = Ριζα[1,Ν]. Πώς συνεχίζουμε; Αριστερά της ρίζας θα βρεθούν τα στοιχεία 1..ρ 1. Επομένως ο αριστερό κόμβος είναι ο ρίζα[1..ρ 1], και ο δεξιός ο ρίζα[ρ+1,ν], και θα πρέπει να συνεχίσουμε κάπως αναδρομικά για τους θυγατρικών αυτών των κόμβων κοκ. Προς τούτο θα χρησιμοποιήσουμε μια διαδικασία Σύνδεση(i,j,ρ: θέσεις στοιχείων) η οποία συνδέει το ριζικό στοιχείο ρ του υπόδενδρου που θα φέρει τα στοιχεία i έως j, με τα όποια θυγατρικά τους, και με το ρ ως πατρικό στοιχείο. Αλγόριθμος «Στατικό ευρετήριο βέλτιστο δένδρο» (τελική εκδοχή) ΚατασκευήΔένδρου( Ρ: πίνακας Ν πιθανοτήτων) Ρίζα: πίνακας [1..Ν+1,0..Ν] ΒέλτιστοΚόστος( Ρ: πίνακας Ν πιθανοτήτων) {... // όπως παραπάνω } Διαδικασία Σύνδεση(i,j,ρ: θέσεις στοιχείων) { // ρίζα για τα στοιχεία [i,j]? κόμβος Ρίζα[i,j] Εάν κόμβος 0 τότε { // σύνδεση κόμβου πατρικός(κόμβος) ρ αριστερός(κόμβος) Ρίζα[i,κόμβος-1] δεξιός(κόμβος) Ρίζα[κόμβος+1,j] // αναδρομή Σύνδεση(i,κόμβος-1, κόμβος) Σύνδεση(κόμβος+1,j, κόμβος) } } { // δένδρα μεγέθους 0 Για i = 1 έως Ν { Ρίζα[i,i-1} 0 } // ρίζα = «κενό» Για j = 1 έως Ν { Ρίζα[j+i,j} 0 } // ρίζα = «κενό» // δένδρα μεγέθους 1 Για k = 1 έως Ν { Ρίζα[k,k] k } } // Βέλτιστο δένδρο ΒέλτιστοΚόστος(Ρ,Ν) Σύνδεση(1,,0) Προσέξτε την πρωτοτιμοδότηση του πίνακα «Ρίζα[, ]», το ότι αυτός συμπληρώνεται από την διαδικασία εύρεσης του βέλτιστου κόστους, και το ότι θεωρούμε ως κενούς κόμβους (ή IL ή ULL), (λ.χ. ως πατρικό της ρίζας), αυτούς με αριθμό «0». Πανεπιστήμιο Κρήτης Τμήμα Επιστήμης Υπολογιστών Γ.Φ. Γεωργακόπουλος ver: 28/3/2014 6