Code con priorità
In questa pagina 5
Una entry è una coppia (chiave, valore) con chiave in un dominio e valore in (getKey(), getValue()). Come ogni ADT (Tipi di dato astrattiUn ADT è definito dalle operazioni e dal loro comportamento, non dalla rappresentazione; interfaccia e implementazione; realizzazione in Python con le classi e in C con header e tipo opaco; esempio di un ADT Frazione.Tipi di dato astratti →) la coda con priorità è definita dalle operazioni e non dall'implementazione.
Definizione (coda con priorità). Una coda con priorità (priority queue) è una collezione di entry le cui chiavi, non necessariamente distinte, rappresentano priorità e provengono da un universo totalmente ordinato (due chiavi qualunque sono sempre confrontabili con , come per i numeri reali: Campi ordinati (Q e R)Gli assiomi di somma, prodotto e ordine comuni a Q e R, da cui derivano le regole di equazioni e disequazioni.Campi ordinati (Q e R) →). Ingressi e uscite si alternano in modo arbitrario, le priorità in ingresso non sono ordinate, le uscite avvengono in ordine di priorità.
interfaccia PriorityQueue<K,V>: size(), isEmpty() insert(k, v) inserisce e restituisce la nuova entry (k, v) min() restituisce una entry con chiave minima, senza toglierla removeMin() restituisce e toglie una entry con chiave minimaPer convenzione chiave minima = priorità massima. Una coda basata sul massimo ha
max()eremoveMax()al posto dimin()eremoveMin().
Esempio. Dopo insert(5,a), insert(2,b), insert(7,c), min() restituisce la entry con chiave e removeMin() la toglie.
Esempio
Partendo da una coda vuota:
| Operazione | Output | Contenuto dopo |
|---|---|---|
insert(5, A) |
||
insert(9, C) |
||
insert(3, B) |
||
insert(7, D) |
||
min() |
invariato | |
removeMin() |
||
size() |
invariato | |
isEmpty() |
falso | invariato |
L'ordine di inserimento è irrilevante per le uscite: contano solo le chiavi. Si noti la differenza con una coda FIFO (Liste, pile e codeRipasso degli ADT elementari: lista index-based (array) e position-based (lista doppiamente concatenata con sentinelle), pila LIFO, coda FIFO, deque, iteratori; interfacce, implementazioni e costi; array estendibile e coda circolare.Liste, pile e code →): nella coda FIFO esce il primo arrivato, qui esce la chiave minima anche se è arrivata per ultima (la entry è stata inserita per terza ed è uscita per prima).
Applicazioni
Liste d'attesa (ad esempio negli aeroporti); scheduling di processi o di richieste di banda per la qualità del servizio; estrazione dei top- da una grande collezione di pattern; algoritmo di Dijkstra per i cammini minimi (vedi Cammini minimi e algoritmo di DijkstraGrafi pesati, lunghezza di un cammino e distanza; sottocammini di un cammino minimo; problema SSSP; algoritmo di Dijkstra con cloud e priority queue, rilassamento degli archi, esempio svolto; correttezza (due lemmi) e complessità O(min(n^2, (n+m) log n)) con lista non ordinata o heap; pesi non negativi.Cammini minimi e algoritmo di Dijkstra →); ordinamento (vedi sotto e Algoritmi di ordinamentoSelection sort e insertion sort (quadratici, con invarianti), merge sort (divide et impera, Theta(n log n)), quick sort (caso pessimo quadratico, medio n log n), heap sort (in loco, Theta(n log n)), ordinamento senza confronti per chiavi intere in un intervallo piccolo, limite inferiore Omega(n log n) per gli algoritmi basati su confronti.Algoritmi di ordinamento →).
Implementazione con liste
Le entry sono tenute in una lista doppiamente concatenata (vedi Liste, pile e codeRipasso degli ADT elementari: lista index-based (array) e position-based (lista doppiamente concatenata con sentinelle), pila LIFO, coda FIFO, deque, iteratori; interfacce, implementazioni e costi; array estendibile e coda circolare.Liste, pile e code →).
Proprietà (code con priorità su liste: costi). Lista non ordinata.
insert: si aggiunge in coda, .min: si scorre tutta la lista tenendo la chiave minore, .removeMin: si trova il minimo e lo si toglie, .Lista ordinata (per chiave crescente).
mineremoveMin: si guarda/toglie il primo nodo, .insert: si scorre fino alla prima chiave e si inserisce prima di essa, .
Esempio. Con entry, removeMin scorre fino a entry in una lista non ordinata e ne guarda una sola in una lista ordinata. Nella lista ordinata insert(6, E) sul contenuto confronta con , , e si ferma davanti a (prima chiave ): il risultato è , con fino a confronti nel caso peggiore (chiave maggiore di tutte).
Le due liste spostano il costo da un'operazione all'altra; lo heap lo bilancia: nessuna operazione è lineare.
Ordinamento con una coda con priorità
Proprietà (ordinamento con una coda con priorità). Per ordinare chiavi: si fanno
inserte poiremoveMin, che restituiscono le chiavi in ordine crescente. Il costo dipende dall'implementazione:
- lista non ordinata: (è il selection sort);
- lista ordinata: (è l'insertion sort);
- heap: (è l'heap sort).
Esempio. Con le chiavi , quattro insert seguiti da quattro removeMin restituiscono .
Da dove escono i conti. Con la lista non ordinata le removeMin scorrono liste di lunghezza , quindi il costo è (somma dei primi interi: SommatorieIl simbolo di sommatoria, le sue proprietà (linearità, additività, cambio di indice) e le somme notevoli di Gauss e geometrica.Sommatorie →). Con la lista ordinata le insert operano su liste lunghe e costano al più . Con lo heap l'-esima operazione costa e (ogni addendo è al più , vedi Esponenziale e logaritmoLa funzione esponenziale a^x (base positiva diversa da 1) e la sua inversa, il logaritmo in base a, con grafici e proprietà.Esponenziale e logaritmo →), quindi . Il grafico confronta con (i due algoritmi quadratici contro heap sort, a meno di costanti):
Grafico interattivo: Confronti di ordinamento con coda con priorità: liste (selection e insertion sort) n(n+1)/2 contro heap (heap sort) 2·n·log2 n; a n = 100 sono 5050 contro 1329, e il divario cresce con n
Per piccolo l'heap può perdere (a : contro ), perché le costanti contano: l'asintotica vale per grande (vedi Notazione asintoticaDefinizioni di O, Omega, Theta e o piccolo con le costanti c ed n0; esempi con costanti esplicite; proprietà (polinomi, esponenziali, logaritmi, somme, implicazioni tra notazioni); sommatorie notevoli; terminologia (logaritmica, lineare, polinomiale, esponenziale).Notazione asintotica →).
Errori comuni
- Dimenticare che le chiavi possono ripetersi:
removeMinrestituisce una entry con chiave minima. - Confondere "priorità massima" con "chiave massima": con la convenzione del corso la priorità massima è la chiave minima.
- Dire che
min()eremoveMin()hanno lo stesso costo in ogni implementazione: nello heap sono e .
Versione ripasso
- Entry: coppia (chiave, valore), con
getKey()egetValue(). Le chiavi non sono necessariamente distinte e provengono da un universo totalmente ordinato (vedi Campi ordinati (Q e R)Gli assiomi di somma, prodotto e ordine comuni a Q e R, da cui derivano le regole di equazioni e disequazioni.Campi ordinati (Q e R) →). - Interfaccia
PriorityQueue<K,V>:size(),isEmpty(),insert(k, v),min()(restituisce una entry con chiave minima senza toglierla),removeMin()(la restituisce e la toglie). - Convenzione: chiave minima = priorità massima. Variante sul massimo:
max()eremoveMax(). - Esempio:
insert(5,A),insert(9,C),insert(3,B),insert(7,D);min()dà ;removeMin()toglie e restano , , . - Diversa dalla coda FIFO (Liste, pile e codeRipasso degli ADT elementari: lista index-based (array) e position-based (lista doppiamente concatenata con sentinelle), pila LIFO, coda FIFO, deque, iteratori; interfacce, implementazioni e costi; array estendibile e coda circolare.Liste, pile e code →): esce la chiave minima anche se è arrivata per ultima. L'ordine di inserimento conta solo per le chiavi uguali.
- Applicazioni: liste d'attesa, scheduling, top-, Dijkstra (vedi Cammini minimi e algoritmo di DijkstraGrafi pesati, lunghezza di un cammino e distanza; sottocammini di un cammino minimo; problema SSSP; algoritmo di Dijkstra con cloud e priority queue, rilassamento degli archi, esempio svolto; correttezza (due lemmi) e complessità O(min(n^2, (n+m) log n)) con lista non ordinata o heap; pesi non negativi.Cammini minimi e algoritmo di Dijkstra →), ordinamento (vedi Algoritmi di ordinamentoSelection sort e insertion sort (quadratici, con invarianti), merge sort (divide et impera, Theta(n log n)), quick sort (caso pessimo quadratico, medio n log n), heap sort (in loco, Theta(n log n)), ordinamento senza confronti per chiavi intere in un intervallo piccolo, limite inferiore Omega(n log n) per gli algoritmi basati su confronti.Algoritmi di ordinamento →).
- Lista non ordinata (doppiamente concatenata):
insert,mineremoveMin. Lista ordinata per chiave crescente:mineremoveMin,insert. Esempio: su ,insert(6,E)confronta con , , e si ferma prima di . - Heap (vedi HeapAlbero binario completo e sua altezza floor(log2 n); heap = albero completo con heap-order property; proprietà (radice minima, cammini non decrescenti); rappresentazione su array con level numbering; insert con up-heap bubbling, removeMin con down-heap bubbling, rimozione di una entry qualsiasi; invarianti e costi Theta(log n); esempio svolto.Heap →):
insert,min,removeMin. Nessuna operazione è lineare. - Ordinamento con coda con priorità (
inserteremoveMin): lista non ordinata dà il selection sort, con e ; lista ordinata dà l'insertion sort, ; heap dà l'heap sort, . Su le uscite sono . - Errori tipici: assumere chiavi distinte; priorità massima = chiave massima; stesso costo per
mineremoveMincon heap ( contro ).