La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Lezione 9 Code con priorità Ordinamento. Sommario Code con priorità Ordinamento –Selection Sort –Bubble Sort –Heap Sort –Quick Sort.

Presentazioni simili


Presentazione sul tema: "Lezione 9 Code con priorità Ordinamento. Sommario Code con priorità Ordinamento –Selection Sort –Bubble Sort –Heap Sort –Quick Sort."— Transcript della presentazione:

1 Lezione 9 Code con priorità Ordinamento

2 Sommario Code con priorità Ordinamento –Selection Sort –Bubble Sort –Heap Sort –Quick Sort

3 Coda con priorità Una coda con priorità è una struttura dati dinamica che permette di gestire una collezione di dati con chiave numerica. Una coda con priorità offre le operazioni di –inserimento: di un elemento nellinsieme –massimo: restituisce lelemento con chiave più grande –cancellazione-massimo: restituisce lelemento con chiave più grande e lo rimuove dalla collezione

4 Applicazioni della Coda con Priorità Le Code con priorità sono strutture dati molto comuni in informatica. Es: –Gestione di processi: ad ogni processo viene associata una priorità. Una coda con priorità permette di conoscere in ogni istante il processo con priorità maggiore. In qualsiasi momento i processi possono essere eliminati dalla coda o nuovi processi con priorità arbitraria possono essere inseriti nella coda. Per implementare una coda con priorità utilizzeremo una struttura dati chiamata heap

5 Nota su gli alberi binari Intuitivamente ( vedremo meglio successivamente ) un albero binario è una struttura dati formata da nodi collegati fra di loro (come per la struttura dati lista) come per una lista, per ogni nodo esiste un unico nodo predecessore a differenza di una lista, ogni nodo è collegato con uno o due nodi successori

6 Heap La struttura dati heap binario è un albero binario quasi completo un albero binario quasi completo è un albero binario riempito completamente su tutti i livelli tranne eventualmente lultimo che è riempito da sinistra a destra

7 Heap Perché un albero binario quasi completo sia uno heap deve valere la seguente: Proprietà dellordinamento parziale dello heap –il valore di un nodo figlio (successore) è minore o uguale a quello del nodo padre (predecessore)

8 Heap Si implementa lalbero tramite un vettore Uno heap A ha un attributo heap-size[A] che specifica il numero di elementi contenuto nello heap nessun elemento in A[1,length[A]] dopo heap-size[A] è un elemento valido dello heap La radice dellalbero è A[1] Lindice del padre di un nodo di posizione i è i/2 Lindice del figlio sinistro di un nodo i è 2 i Lindice del figlio destro di un nodo i è 2 i +1

9 Visualizzazione di uno heap

10 Pseudocodice Operazioni Heap Parent(i) 1 return i/2 Left(i) 1 return 2 i Right(i) 1 return 2 i + 1

11 Mantenimento proprietà heap A seguito di varie operazioni sullo heap può accadere che un nodo violi la proprietà dello heap La procedura Heapify prende in ingresso uno heap A e lindice i di un nodo che potenzialmente viola la proprietà e ristabilisce la proprietà di ordinamento parziale sullintero heap si assume che i sottoalberi figli del nodo i siano radici di heap che rispettano la proprietà di ordinamento parziale

12 Spiegazione Lidea è di far affondare il nodo che viola la proprietà di ordinamento parziale fino a che la proprietà non viene ripristinata per fare questo si determina il nodo figlio più grande e si scambia il valore della chiave fra padre e figlio si itera ricorsivamente il procedimento sul nodo figlio sostituito

13 Pseudocodice Heapify Heapify(A,i) 1 l Left(i) 2 r Right(i) 3 if l heap-size[A] e A[l]>A[i] 4thenlargest l 5elselargest i 6 if r heap-size[A] e A[r]>A[largest] 7thenlargest r 8 if largest i 9thenscambia A[i] A[largest] 10Heapify(A,largest)

14 Visualizzazione Heapify i i

15 Nota sugli alberi binari Un albero binario completo di altezza h ha 2 h+1 -1 nodi infatti intuitivamente: – un albero binario completo di altezza 0 ha un unico nodo: la radice –un albero binario completo di altezza 1 è composto dalla radice e dai suoi due figli –allaumentare di un livello ogni figlio genera altri due figli e quindi si raddoppia il numero di nodi del livello precedente Ogni livello di un albero binario completo contiene tanti nodi quanti sono contenuti in tutti i livelli precedenti +1 –infatti passando da h a h+1 si passa da 2 h+1 -1 a 2 h+2 -1 nodi ovvero a 2(2 h+1 ) -1 nodi quindi il nuovo livello ha aggiunto 2 h+1 nodi ad un albero che prima ne conteneva 2 h+1 -1

16 Tempo di calcolo di Heapify Le istruzioni per determinare il maggiore fra i, l e r impiegano un tempo (1) Ricorsivamente si chiama Heapify su uno dei sottoalberi radicati in l o r Il sottoalbero dei figli hanno al più dimensione 2n/3 –il caso peggiore è quando lultimo livello è pieno per metà ovvero uno dei sottoalberi è completo Il tempo di esecuzione è pertanto: T(n)=T(2n/3)+ (1) per il Teorema Principale si ha T(n)= (lg n)

17 Costruzione di uno heap Si può usare la procedura Heapify in modo bottom- up, cioè a partire dai livelli più bassi dellalbero, per convertire un Array in uno Heap Gli elementi A[ n/ n ] sono tutte foglie dellalbero e pertanto ognuno di essi è già uno heap di 1 elemento Si inizia dagli elementi padri dei nodi A[ n/ n ] Dato che procediamo in modo bottom-up allora sicuramente i sottoalberi di un nodo sottoposto a Heapify sono heap

18 Pseudocodice Costruzione Heap Build-Heap(A) 1 heap-size[A] Length[A] 2 for i length[A]/2 downto 1 3 do Heapify(A,i)

19 Visualizzazione Costruzione

20 Coda con priorità con heap Risulta semplice implementare le varie operazioni di una coda con priorità utilizzando uno heap –Extract Max: basta restituire la radice dello heap –Heap Extract Max: dopo la restituzione dellelemento massimo, posiziona lultimo elemento dello heap nella radice ed esegue Heapify per ripristinare la proprietà di ordinamento parziale –Heap Insert: la procedura inserisce il nuovo elemento come elemento successivo allultimo e lo fa salire fino alla posizione giusta facendo scendere tutti padri

21 Pseudo codice operazioni di coda con priorità Heap-Extract-Max(A) 1 max A[1] 2 A[1] A[heap-size[A]] 3 heap-size[A] heap-size[A]-1 4 Heapify(A,1) 5 return max Heap-Insert(A,key) 1 heap-size[A] heap-size[A]+1 2 i heap-size[A] 3 while i>1 e A[Parent(i)]

22 Visualizzazione Heap Insert / Key:15 16 /

23 Ordinamento Il problema consiste nellelaborare insiemi di dati costituiti da record Esiste un elemento del record è detto chiave Lobiettivo dei metodi di ordinamento consiste nel riorganizzare i dati in modo che le loro chiavi siano disposte secondo un ordine specificato (generalmente numerico o alfabetico)

24 Metodi Si distinguono metodi: –interni: se linsieme di dati è contenuto nella memoria principale –esterni: se linsieme di dati è immagazzinato su disco o nastro Per metodi interni è possibile laccesso casuale ai dati, mentre per i metodi esterni è possibile solo laccesso sequenziale o a grandi blocchi

25 Tipo di ordinamento Si distinguono i metodi di ordinamento in stabili o non stabili. Un metodo di ordinamento si dice stabile se preserva lordine relativo dei dati con chiavi uguali allinterno del file da ordinare Se si usa un metodo stabile per ordinare per anno di corso una lista di studenti già ordinata alfabeticamente otterremo una lista in cui gli studenti dello stesso anno sono ordinati alfabeticamente

26 Tipo di ordinamento Si distinguono i metodi di ordinamento in diretti o indiretti. Un metodo di ordinamento si dice diretto se accede allintero record del dato da confrontare, indiretto se utilizza dei riferimenti (puntatori) per accedervi Metodi indiretti sono utili quando si devono ordinare dati di grandi dimensioni In questo modo non è necessario spostare i dati in memoria ma solo i puntatori ad essi.

27 Tipo di ordinamento Si distinguono i metodi di ordinamento sul posto e non, che fanno cioè uso di strutture ausiliare Un metodo si dice che ordina sul posto se durante lelaborazione riorganizza gli elementi del vettore in ingresso allinterno del vettore stesso Se il metodo, per poter operare, ha necessità di allocare un vettore di appoggio dove copiare i risultati parziali o finali dellelaborazione (della stessa dimensione del vettore in ingresso) abbiamo il secondo caso

28 Selection Sort E uno degli algoritmi più semplici Il principio è: –si determina lelemento più piccolo di tutto il vettore –lo si scambia con lelemento in prima posizione del vettore –si cerca il secondo elemento più grande –lo si scambia con lelemento in seconda posizione del vettore –si procede fino a quando lintero vettore è ordinato Il nome deriva dal fatto che si seleziona di volta in volta il più piccolo elemento fra quelli rimanenti

29 Pseudocodice per SelectionSort SelectionSort(A) 1for i 1 to length[A] 2 do min i 3for j i+1 to length[A] 4do if(A[j]

30 Caratteristiche del SelectionSort Il tempo di calcolo è T(n)= (n 2 ) infatti: –per ogni dato di posizione i si eseguono n-1-i confronti –il numero totale di confronti è pertanto (posto j= n-1-i ) j=n-1..1 j = n(n-1)/2 = (n 2 ) Più precisamente il Selection Sort effettua circa n 2 /2 confronti e n scambi

31 Caratteristiche del SelectionSort Uno svantaggio è che il tempo di esecuzione non dipende (in modo significativo) dal grado di ordinamento dei dati iniziali Un vantaggio è che ogni elemento è spostato una sola volta. –Se è necessario spostare i dati, allora per dati molto grandi questo è lalgoritmo che asintoticamente effettua il minor numero di spostamenti possibili. –Se il tempo di spostamento è dominante rispetto al tempo di confronto diventa un algoritmo interessante

32 BubbleSort E un metodo elementare Il principio di funzionamento è: –si attraversa il vettore scambiando coppie di elementi adiacenti –ci si ferma quando non è più richiesto alcuno scambio Il nome deriva dal seguente fenomeno: –quando durante lattraversamento si incontra lelemento più piccolo non ancora ordinato questo viene sempre scambiato con tutti, affiorando fino alla posizione giusta come una bolla –nel processo gli elementi maggiori affondano e quelli più leggeri salgono a galla

33 PseudoCodice per il BubbleSort BubbleSort(A) 1for i 1 to length[A] 2do for j length[A] downto i-1 4 do if(A[j-1] > A[j]) 5 then scambia A[j-1] A[j]

34 Caratteristiche del BubbleSort Il tempo di calcolo è T(n)= (n 2 ) infatti: –per ogni dato di posizione i si eseguono i confronti e i scambi –il numero totale di confronti è pertanto i=1..n-1 i = n(n-1)/2 = (n 2 ) Il Bubble Sort effettua circa n 2 /2 confronti e n 2 /2 scambi in generale è peggiore del selection sort

35 Heap Sort Il metodo di ordinamento Heap Sort sfrutta la proprietà di ordinamento parziale dello Heap Lidea è di selezionare lelemento più grande, eliminarlo dallo heap e poi utilizzare la procedura Heapify per ripristinare la proprietà di ordinamento parziale in pratica piuttosto che eliminare gli elementi si inseriscono come elemento più prossimo oltre i limiti dello heap

36 Pseudocodice per HeapSort HeapSort(A) 1BuildHep(A) 2 for i length[A] downto 2 3do scambia A[1] A[i] 4heap-size[A] heap-size[A]-1 5Heapify(A,1)

37 Visualizzazione HeapSort

38 Tempo di Calcolo dellHeapSort Lalgoritmo chiama n-1 volte la procedura Heapify Si deve determinare il tempo di calcolo di Heapify Abbiamo visto che per Heapify si ha T(n)= (lg n) Pertanto il tempo di calcolo per HeapSort è: T(n)= (n lg n)

39 QuickSort Il QuickSort, come il MergeSort, è un algoritmo basato sul paradigma Divide et Impera Fasi: –Divide: il vettore A[p..r] è riorganizzato in modo da avere due sottosequenze A[p..q] e A[q+1..r] tali che qualsiasi elemento in A[p..q] è minore di un qualsiasi elemento in A[q+1..r]. Lindice q è calcolato da questa procedura di partizionamento –Impera: le due sottosequenze sono ordinate ricorsivamente –Combina: non ce ne è bisogno. Infatti, poiché le sottosequenze sono già ordinate internamente e qualsiasi elemento in A[p..q] è minore di un qualsiasi elemento in A[q+1..r], lintero vettore A[p..r] è subito ordinato

40 PseudoCodice QuickSort(A,p,r) 1if p

41 Spiegazione Intuitiva della Procedura Partition Si prende un elemento, ad es. il primo elemento della prima sottosequenza, come elemento perno si vuole dividere il vettore A in due sottosequenze: –nella prima devono esserci solo elementi <= 5 –nella seconda solo elementi >= x=A[p]=5 p r

42 Spiegazione Intuitiva della Procedura Partition Si fanno crescere due regioni da entrambi gli estremi, utilizzando gli indici i,j a partire dagli estremi i j Elementi <=5 Elementi >=5 i j

43 Spiegazione Intuitiva della Procedura Partition Mentre le due regioni crescono si verifica il valore degli elementi Se un elemento non deve appartenere alla regione in cui si trova (o se lelemento ha un valore eguale al valore perno) si smette di far crescere la regione Quando non è possibile far crescere nessuna delle due regioni si scambiano gli elementi fra loro i j Elementi <=5 Elementi >=5 i j Elementi <=5 Elementi >=5 7337

44 Spiegazione Intuitiva della Procedura Partition Quando i diventa maggiore di j allora abbiamo completato le due regioni La procedura termina i j Elementi <=5Elementi >=5

45 PseudoCodice per Partition Partition(A,p,r) 1x A[p] 2i p-1 3j r+1 4while TRUE 5do repeat j j-1 6 until A[j] x 7 repeat i i+1 8 until A[i] x 9if i < j 10 thenscambia A[i] A[j] 11else return j

46 Visualizzazione ij ij ij ij ij ij ij ij ij ij

47 Prestazioni del QuickSort Il tempo di esecuzione del QuickSort dipende dal fatto che il partizionamento sia più o meno bilanciato il partizionamento dipende dagli elementi pivot. Se il partizionamento è bilanciato si hanno le stesse prestazioni del MergeSort altrimenti può essere tanto lento quanto lInsertionSort

48 Caso peggiore Il caso di peggior sbilanciamento si ha quando il partizionamento produce due sottosequenze di lunghezza 1 e n-1 Il partizionamento richiede un tempo (n) e il passo base della ricorsione richiede T(1)= (1) pertanto: T(n)=T(n-1)+ (n) ad ogni passo si decrementa di 1 la dimensione dellinput, occorreranno pertanto n passi per completare la ricorsione T(n)= k=1..n (k) = ( k=1..n k) = (n 2 )

49 Caso migliore Il caso migliore si ha se ad ogni partizionamento di divide in due sottosequenze di dimensione identica linput in questo caso si ha, come nel caso del MergeSort –T(n)=2T(n/2)+ (n) ovvero, per il Teorema Principale: –T(n)= (n lg n)

50 Caso medio Per avere una intuizione di cosa accade nel caso medio può risultare utile introdurre il concetto di albero di ricorsione

51 Albero di ricorsione Un albero di ricorsione è un modo di visualizzare cosa accade in un algoritmo divide et impera Letichetta della radice rappresenta il costo non ricorsivo della fase divide e combina la radice ha tanti figli quanti sottoproblemi generati ricorsivamente ogni sottoproblema ha come etichetta il costo della fase divide e combina del problema per la nuova dimensione per ogni livello si indica la somma dei costi si conteggia il numero di livelli necessari per terminare la ricorsione

52 Esempio di albero di ricorsione Per T(n)=2T(n/2)+n 2 si ha: n2n2 T(n/2) n2n2 (n/2) 2 T(n/4)

53 Esempio di albero di ricorsione n2n2 (n/2) 2 (n/4) 2 lg n n2n2 1/2 n 2 1/4 n 2... Totale = n 2 i=1.. lg n 1/2 i < n 2 i=1.. 1/2 i =2 n 2 Totale = (n 2 )

54 Esempio di albero di ricorsione Per il calcolo dellaltezza dellalbero di ricorsione, ovvero del numero di passi necessario per terminare la ricorsione, si considera il cammino più lungo dalla radice ad una foglia n 2 >> (1/2n) 2 >> ((1/2) 2 n) 2 >> ((1/2) 3 n) 2 >>... >> 1 Dato che ((1/2) i n) 2 = 1 2 i = n log 2 2 i = log 2 n i = lg n ovvero h= lg n

55 Concetti intuitivi sul caso medio Si consideri un partizionamento abbastanza sbilanciato che divida il problema in due sotto sequenze di dimensione 1/10 e 9/10 T(n)=T(n/10)+T(9n/10)+n dallalbero di ricorsione si ha che ogni livello costa n il numero di livelli è: –n>>9/10 n >>(9/10) 2 n >>(9/10) 3 n>>…>>1 –ovvero (9/10) i n = 1 –n = (10/9) i –i = log 10/9 n

56 Albero di ricorsione n (n/10)(9n/10) (n/100)(9n/100) (81n/100) log 10/9 n n n n... Totale = (n lg n)

57 Concetti intuitivi sul caso medio Con un partizionamento di 1 a 9 il quicksort viene eseguito in un tempo asintoticamente eguale al caso migliore di partizionamento bilanciato Anche una suddivisione 1 a 99 mantiene la proprietà di esecuzione in tempo n lg n Il motivo è che qualsiasi suddivisione in proporzioni costanti produce un albero di altezza lg n in cui ogni livello costa (n) Tuttavia non possiamo aspettarci un partizionamento costante nei casi reali

58 Concetti intuitivi sul caso medio Si suppone che in media si abbiano delle partizioni buone e cattive distribuite durante lo svolgimento dellalgoritmo si suppone per semplificare che le partizioni buone si alternino a quelle cattive si suppone che la partizione buona sia la migliore possibile, cioè quella derivante da un bilanciamento perfetto e quella cattiva la peggiore possibile, cioè quella derivante da una partizione 1, n-1

59 Concetti intuitivi sul caso medio Si ha pertanto un primo passo in cui si partiziona il problema in due sottosequenze 1 e n-1 di costo n poi un secondo passo in cui –si risolve in tempo costante il problema sulla sottosequenza unitaria –si partiziona ulteriormente la sequenza di lunghezza n-1 in due sottosequenze di (n-1)/2 e (n-1)/2 elementi ad un costo di n-1 Il risultato è di aver raddoppiato il numero di passi rispetto al caso di partizionamento ottimo, ma di essere sostanzialmente arrivati nella stessa situazione di buon partizionamento (n/2 contro (n- 1)/2) con un costo complessivo (n)

60 Concetti intuitivi sul caso medio In sintesi il costo del QuickSort per un caso medio in cui si alternano partizioni buone a cattive è dello stesso ordine di grandezza del caso migliore, e cioè O(n lg n) anche se con costanti più grandi

61 Algoritmo Randomizzato Il caso medio si ottiene quando una qualsiasi permutazione dei dati in ingresso è equiprobabile tuttavia nei casi reali questo può non essere vero: ad esempio ci sono situazioni in cui i dati sono parzialmente ordinati in questi casi le prestazioni del quicksort peggiorano piuttosto che ipotizzare una distribuzione dei dati in ingresso è possibile imporre una distribuzione si chiama randomizzato un algoritmo il cui comportamento è determinato non solo dallingresso ma anche da un generatore di numeri casuale

62 Randomized QuickSort 1 Prima di avviare la procedura di sort si esegue una permutazione casuale degli elementi del vettore di ingresso Il calcolo della permutazione deve avere un costo O(n) Nessuna sequenza in ingresso comporta il caso peggiore: tuttavia si può avere una cattiva permutazione prodotta dal generatore casuale Tuttavia si può garantire che si generino pochissime permutazioni cattive in media

63 PseudoCodice per la permutazione casuale Randomize(A,p,r) 1for i 1 to length[A] 2doj Random(p,r) 3scambia A[i] A[j]

64 Randomized QuickSort 2 Ad ogli passo, prima di partizionare larray si scambia A[p] con un elemento scelto a caso in A[p..r] si sceglie cioè lelemento pivot in modo casuale ad ogni partizionamento Questo assicura che il pivot sia in modo equiprobabile uno qualsiasi degli elementi in A[p..r] Ci si aspetta cosi che in media la suddivisione dellarray sia ben bilanciata

65 PseudoCodice Randomized-Partition(A,p,r) 1i Random(p,r) 2scambia A[p] A[i] 3return Partition(A,p,r) Randomized-QuickSort(A,p,r) 1 if p


Scaricare ppt "Lezione 9 Code con priorità Ordinamento. Sommario Code con priorità Ordinamento –Selection Sort –Bubble Sort –Heap Sort –Quick Sort."

Presentazioni simili


Annunci Google