Divide et Impera Quicksort Mergesort

Slides:



Advertisements
Presentazioni simili
Algoritmi di ordinamento
Advertisements

Uso avanzato di C.
Lez. 10a1 Universita' di Ferrara Facolta' di Scienze Matematiche, Fisiche e Naturali Laurea Specialistica in Informatica Algoritmi Avanzati Strategie per.
Code con priorità Ordinamento
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Usa la tecnica del.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti ottimi.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Usa la tecnica del.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti lineari.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Usa la tecnica del.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti lineari.
Mergesort1 if (n>1) /* la collezione contiene almeno due elementi. */ {1. Dividi la collezione in due di circa la metà degli elementi. 2. chiamata ricorsiva.
Alberi binari Definizione della struttura dati: struct tree { };
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 4 Ordinamento:
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 4 Ordinamento:
Algoritmi e Strutture Dati
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Usa la tecnica del.
CORSO DI PROGRAMMAZIONE II
CORSO DI PROGRAMMAZIONE II Operazioni su alberi ordinati e non
Alberi di Ricorrenza Gli alberi di ricorrenza rappresentano un modo conveniente per visualizzare i passi di sostitu- zione necessari per risolvere una.
Algoritmi e Strutture Dati III. Algoritmi di Ordinamento
Progetto di algoritmi: metodologia "Divide et Impera"
QuickSort Quick-Sort(A,s,d) IF s < d THEN q = Partiziona(A,s,d) Quick-Sort(A,s,q-1) Quick-Sort(A,q + 1,d)
Soluzione 6: Algoritmo Quicksort
Algoritmi e Strutture Dati
Paola Disisto, Erika Griffini, Yris Noriega.  Insieme ordinato di operazioni non ambigue ed effettivamente computabili che, quando eseguito, produce.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 4 Ordinamento:
Lo strano mondo degli algoritmi di ordinamento Algoritmi.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 4 Ordinamento:
Problema dell’Ordinamento. Problema dell’ordinamento Formulazione del problema –Si vuole ordinare una lista di elementi secondo una data proprietà P Esempio:
Quick sort void quick_sort (int a[],int l, int r) { int i, j,v, t; if (r>l) { v=a[r];//L’elemento “pivot” è quello più a dx i=l-1; j=r; // i scorre da.
L’ALGORITMO Un algoritmo è un procedimento formale che risolve un determinato problema attraverso un numero finito di passi. Un problema risolvibile mediante.
Divide et Impera Parte 11 - Risoluzione di problemi per divisione in sottoproblemi “bilanciati” Corso A: Prof. Stefano Berardi
6/11/01Ordinamento 1 Un esempio di algoritmi: ordinamento.
Statistiche d'ordine Moreno Marzolla Dip. di Scienze dell'Informazione Università di Bologna
Huffman Canonico: approfondimento. Come abbiamo visto, Huffman canonico ci permette di ottenere una decompressione più veloce e con un uso più efficiente.
Esercitazioni di Prog. II (esercizi su alberi ennari)
Progettare algoritmi veloci usando strutture dati efficienti
Ordinamento.
Divide et Impera Quicksort Mergesort Charles Antony Richard Hoare
Algoritmi Avanzati a.a.2011/2012 Prof.ssa Rossella Petreschi
Algoritmi Avanzati Prof.ssa Rossella Petreschi
Tipo di dato: array Un array è un tipo di dato usato per memorizzare una collezione di variabili dello stesso tipo. Per memorizzare una collezione di 7.
Usi (meno scontati) della visita DFS
Algoritmi e Strutture Dati
Quick Sort: Esempio Si consideri il seguente vettore, v, di n=10 elementi: i=inf j=sup Scegliamo come pivot.
Programmazione e Laboratorio di Programmazione
Scrivere programmi corretti
Ordinamento in tempo lineare
Algoritmo InsertionSort
Programmazione e Laboratorio di Programmazione
Schema generale, visita in ampiezza e profondità.
Alberi n-ary Lezioni di C.
Algoritmi Avanzati a.a.2011/2012 Prof.ssa Rossella Petreschi
Usi (meno scontati) della visita DFS
Progettare algoritmi veloci usando strutture dati efficienti
esercizi alberi binari
APPUNTI SUL LINGUAGGIO C
comprensione e modifica di codice
comprensione e modifica di codice
Esercizio Dato un albero binario, definiamo altezza minimale di un nodo v la minima distanza di v da una delle foglie del suo sottoalbero, definiamo invece.
Programmazione e Laboratorio di Programmazione
MergeSort Usa la tecnica del divide et impera:
Verifica bilanciamento nel numero dei nodi: definizioni.
concetti ed applicazioni
Programmazione e Laboratorio di Programmazione
Ricerca 01/08/2019 package.
HeapSort Stesso approccio incrementale del selectionSort Tipo di dato
Algoritmi di ordinamento
Programmazione e Laboratorio di Programmazione
Corso di Fondamenti di Informatica
Transcript della presentazione:

Divide et Impera Quicksort Mergesort Charles Antony Richard Hoare (1936(?)) (senior researcher with Microsoft Research in Cambridge ) Computer Jurnal 5,1,1962 John von Neumann(1903-1957) Nel 1944, il suo rapporto interno “First Draft of a Report on the EDVAC” contiene tra l’altro, il mergesort come programma di ordinamento

Esempio di Divide et Impera Due fasi Quicksort Esempio di Divide et Impera Due fasi la fase di partizione Dividi la lista degli elementi in due fase di ordinamento conquista le due metà!

Quicksort La fase di partizionamento Scegli un pivot Trovane la posizione giusta nella lista tutti gli elementi a sinistra sono minori tutti gli elementi a destra sono maggiori o uguali < pivot pivot >= pivot

Quicksort Impera Applica lo stesso algoritmo alle due parti < pivot

Quicksort: Pseudocodice void qSort(int a[], int start, int end) { int p; if (start >= end) return; /*la ricorsione termina quando c’è al più un elemento*/ dividi il vettore a in due sottovettori, i modo tale che la prima metà contenga gli elementi più piccoli del pivot, l’altra i più grandi e il pivot si trovi nella giusta posizione p nel vettore ordinato Divide qSort(a,start, p-1); qSort(a,p+1,end); Impera }

Quicksort void qSort1(int a[],int start, int end) /* ordina in ordine crescente un vettore di interi * prec: (a !=NULL) * postc : a[i] <= a[i+1], per start<=i<end.*/ { int m; if (start < end) /* c’è più di un elemento*/ {m = partition(a,start,end); /*m è la posizione del pivot*/ /*Qui (a[start],…,a[m-1] < a[m]<= a[m+1],…,a[end]*/ qSort1(a,start, m-1); qSort1(a,m+1,end); }

Come realizzare il partizionamento Come realizzare il partizionamento? Prima di tutto prendiamo come pivot il primo elemento, per semplicità. Una prima idea: scorro l’array e confrontando gli elementi con il pivot e sistemando all’inizio tutti i più piccoli e a seguire i più grandi: ? < pivot >=pivot a start j j+1 k-1 end Gli indici j e k servono per individuare le due “zone”: quella dei più piccoli e quella dei più grandi, a[k] è il prossimo elemento da confrontare con il pivot e “sistemare” in conseguenza

(a[start+1],…,a[j] < pivot ) && (a[j+1],…,a[k-1] >= pivot) Quicksort - partizione1 pivot = a[start] INVARIANTE di ciclo : un’asserzione che deve essere vera all’entrata per ogni esecuzione del ciclo. Nello specifico, l’invariante del ciclo di partizionamento: (a[start+1],…,a[j] < pivot ) && (a[j+1],…,a[k-1] >= pivot) ? < pivot >=pivot a start j j+1 k-1 end

Se a[k] =x >= pivot incrementa k Quicksort - partizione1 < pivot >=pivot ? y x start j j+1 k-1 k end Se a[k] =x >= pivot incrementa k >=pivot ? < pivot y x start j j+1 k-1 k end e l’invariante è ancora vero

e l’invariante è ancora vero Quicksort - partizione1 L’invariante del ciclo di partizionamento: < pivot >= pivot ? y z x start j j+1 k-1 k end Se a[k] = x < pivot, scambia a[j+1] = y con a[k] = x e incrementa j e k < pivot >= pivot ? x z y start j j+1 k-1 k end e l’invariante è ancora vero

< pivot >= pivot ? pivot start j k-1 end j = start; /* il pivot è a[start] */ for (k= start+1;k <= end; k++) /* invariante: (a[start+1],…,a[j] < a[start])&& (a[j+1],…,a[k-1] >= a[start])*/ if (a[k] < a[start]) scambio(&a[++j],&a[k]); Alla fine del ciclo for di partizionamento: >= pivot < pivot pivot start j end

< pivot pivot < pivot Quicksort - partizione1 >= pivot < pivot pivot start j end Con un ulteriore scambio: scambio(&a[start],&a[j]); < pivot >= pivot pivot start j end

int partition1(int *a, int in, int fin) Quicksort - partizione1 int partition1(int *a, int in, int fin) /* divide il vettore a in due sottovettori il primo con gli elementi da start a j-1 e il secondo con quelli da j+1 a end e in modo tale che (P)(a[in],…,a[j-1] < a[j]<= a[j+1],…,a[fin]*/ postc: restituisce l'indice j tale che valga P*/ { int j = in, k; for (k = in+1; k <= fin; k++) /* invariante: (a[in+1],...,a[j] < a[in]) && (a[j+1],...,a[k-1] >= a[in]) */ if (a[k] < a[in]) scambia(&a[++j],&a[k]); scambia(&a[j],&a[in]); return j; }

Chiamata partition1(a,0, 7); Quicksort - partizione1 50 60 20 90 10 80 70 30 { int j = in, k; for (k = in+1; k <= fin; k++) /* invariante: (a[in+1],...,a[j] < a[in]) && (a[j+1],...,a[k-1] >= a[in]) */ if (a[k] < a[in]) scambia(&a[++j],&a[k]); scambia(&a[j],&a[in]); return j; } 0 1 2 3 4 5 6 7 j k 50 60 20 90 10 80 70 30 0 1 2 3 4 5 6 7 j k 50 20 60 90 10 80 70 30 0 1 2 3 4 5 6 7 j k

for (k = in+1; k <= fin; k++) /* invariante: Quicksort - partizione1 50 20 60 90 10 80 70 30 { int j = in, k; for (k = in+1; k <= fin; k++) /* invariante: (a[in+1],...,a[j] < a[in]) && (a[j+1],...,a[k-1] >= a[in]) */ if (a[k] < a[in]) scambia(&a[++j],&a[k]); scambia(&a[j],&a[in]); return j; } 0 1 2 3 4 5 6 7 j k 50 20 60 90 10 80 70 30 0 1 2 3 4 5 6 7 j k 50 20 10 90 60 80 70 30 0 1 2 3 4 5 6 7 j k

for (k = in+1; k <= fin; k++) /* invariante: Quicksort - partizione1 50 20 10 90 60 80 70 30 { int j = in, k; for (k = in+1; k <= fin; k++) /* invariante: (a[in+1],...,a[j] < a[in]) && (a[j+1],...,a[k-1] >= a[in]) */ if (a[k] < a[in]) scambia(&a[++j],&a[k]); scambia(&a[j],&a[in]); return j; } 0 1 2 3 4 5 6 7 j k 50 20 10 90 60 80 70 30 0 1 2 3 4 5 6 7 j k 50 20 10 90 60 80 70 30 0 1 2 3 4 5 6 7 j k

for (k = in+1; k <= fin; k++) /* invariante: Quicksort - partizione1 { int j = in, k; for (k = in+1; k <= fin; k++) /* invariante: (a[in+1],...,a[j] < a[in]) && (a[j+1],...,a[k-1] >= a[in]) */ if (a[k] < a[in]) scambia(&a[++j],&a[k]); scambia(&a[j],&a[in]); return j; } 50 20 10 90 60 80 70 30 0 1 2 3 4 5 6 7 j k 50 20 10 30 60 80 70 90 0 1 2 3 4 5 6 7 j k 30 20 10 50 60 80 70 90 0 1 2 3 4 5 6 7 j k

m = partition(a,start,end); /*in m il valore di j*/ Quicksort - partizione1 30 20 10 50 60 80 70 90 0 1 2 3 4 5 6 7 j m = partition(a,start,end); /*in m il valore di j*/ /*Qui (a[start],…,a[m-1] < a[m]<= a[m+1],…,a[end]*/ qSort1(a,start, m-1); qSort1(a,m+1,end); ...

Quicksort - albero delle chiamate qSort1(a,0, 7); qSort1(a,0, 2); qSort1(a,4, 7); qSort1(a,0,1); qSort1(a,3, 2); qSort1(a,4,3); qSort1(a,5,7); qSort1(a,0,0); qSort1(a,1,1); qSort1(a,5,5); qSort1(a,6,7); qSort1(a,6,5); qSort1(a,7,7);

Quicksort - complessità Supponiamo il numero degli elementi n sia pari a 2h e che si ottenga un albero delle chiamate di altezza h. In ogni divisione supponiamo che i due sottovettori hanno circa lo stesso numero di elementi Allora al livello 0 num. chiamate = 1, ciascuna di costo circa 2h, tot. ≤ c02h al livello 1 num. chiamate = 2, ciascuna di costo circa 2h-1,tot. ≤ c12h al livello 2 num. chiamate = 4, ciascuna di costo circa 2h-2,tot. ≤ c22h . . . al livello h num. chiamate ≤ 2h , ciascuna di costo costante, tot. ≤ ch2h Sommando, il costo è maggiorabile da ch2h , per un’opportuna costante c, cioè costo <= cnlog2(n)

for (k = in+1; k <= fin; k++) /* invariante: Quicksort - complessità: il caso di elementi tutti uguali 50 50 50 50 50 50 50 50 { int j = in, k; for (k = in+1; k <= fin; k++) /* invariante: (a[in+1],...,a[j] < a[in]) && (a[j+1],...,a[k-1] >= a[in]) */ if (a[k] < a[in]) scambia(&a[++j],&a[k]); scambia(&a[j],&a[in]); return j; } 0 1 2 3 4 5 6 7 j k 50 50 50 50 50 50 50 50 0 1 2 3 4 5 6 7 j k qSort1(a,n,0, -1); qSort1(a,n,1,end); ...

Quicksort - complessità: l’albero delle chiamate nel caso di elementi tutti uguali. qSort1(a,0, 7); qSort1(a,0, -1); qSort1(a,1, 7); qSort1(a,2, 7); qSort1(a,1, 0); qSort1(a,3, 7); qSort1(a,2, 1); qSort1(a,4, 7); qSort1(a,3, 2);

Quicksort - complessità, il caso di n elementi tutti uguali Abbiamo al livello 0 num. chiamate = 1, ciascuna di costo circa n, tot. ≤ c0n al livello 1 num. chiamate = 2, una trascurabile e una di costo circa n-1,tot. ≤ c1(n-1) al livello 2 num. chiamate = 2, una trascurabile e una di costo circa n-2,tot. ≤ c2(n-2). . . al livello n-2 num. chiamate =2 , una trascurabile e una di costo costante d,tot. ≤ cn-1d Sommando si ottiene che il costo è maggiorabile da cn(n+1)/2 , per un’opportuna costante c.

void qSort2(int a[], int start, int end) {int j; Quicksort - versione alternativa void qSort2(int a[], int start, int end) {int j; if (start < end) /* c’è più di un elemento*/ {j = partition2(a,start,end) /*qui (a[start],…,a[j-1] <= a[j]<= a[j+1],…,a[end]*/ qSort2(a,start, j-1); qSort2(a,j+1,end);} }

a[i+1] e a[j] sono gli elementi da confrontare con il pivot Quicksort - partizione2 pivot = a[start] Come migliorare il partizionamento? Non possiamo pensare di ridurre il numero di confronti ma potremmo puntare a ridurre gli scambi seconda idea: scorro l’array da sinistra verso destra finchè trovo un elemento maggiore o uguale del pivot, scorro il vettore da destra finchè trovo un elemento più piccolo o uguale del pivot, ora scambio questi due e riprendo lo scorrimento. ? > pivot < pivot a start i i+1 j j+1 end a[i+1] e a[j] sono gli elementi da confrontare con il pivot e “sistemare” in conseguenza

INVARIANTE del ciclo di partizionamento: Quicksort - partizione2 pivot = a[start] INVARIANTE del ciclo di partizionamento: (a[start+1],…,a[i] <= pivot)&& (a[j+1],…,a[end] >= pivot) ? ≥pivot ≤ pivot start i i+1 j j+1 end a[i+1] e a[j] sono gli elementi da confrontare con il pivot e “sistemare” in conseguenza

INVARIANTE del ciclo di partizionamento: Quicksort - partizione2 INVARIANTE del ciclo di partizionamento: (a[start+1],…,a[i] <= pivot)&&(a[j+1],…,a[end] >= pivot) ≥pivot ≤ pivot pivot ? start i j end i =start+1, j = end; fintanto che a[i] < pivot incrementa i fintanto che a[j] > pivot decrementa j

Quicksort - partizione2 ? <= pivot >=pivot pivot x y start i j end i =start+1, j = end; fintanto che a[i] < pivot incrementa i fintanto che a[j] > pivot decrementa j scambia a[i] e a[j] alla fine dei due cicli a[i] = x >= pivot e a[j] = y <= pivot Dopo lo scambio: <= pivot >=pivot ? pivot y x start i j end

int partition2(int a[], int start, int end) Quicksort - partizione2: il codice int partition2(int a[], int start, int end) {int pivot = a[start], i =start, j = end+1; for (;;) { /* invariante: (a[start+1],…,a[i] < =pivot)&& (a[j+1],…,a[end] >= pivot)*/ while ((a[++i] < pivot)&&(i<=end)); while (a[--j] > pivot; if (i > j) break; scambia(&a[j],&a[i]); } scambia(&a[start],&a[j]); /*(a[start],…,a[j-1] <= a[j]<= a[j+1],…,a[end]*/ return j;}

{pivot = a[start], i =start, j = end+1; for (;;) Quicksort - partizione2: esempio di esecuzione {pivot = a[start], i =start, j = end+1; for (;;) /* invariante: (a[start+1],…,a[i] <=pivot) && (a[j+1],…,a[end] >= pivot)*/ while ((a[++i] < pivot) && (i<=end)) ; while (a[--j] > pivot ; if (i > j) break; scambio(&a[j],&a[i]); } 50 60 20 90 10 80 70 30 0 1 2 3 4 5 6 7 i j 50 60 20 90 10 80 70 30 0 1 2 3 4 5 6 7 i j 50 30 20 90 10 80 70 60 0 1 2 3 4 5 6 7 i j

{pivot = a[start], i =start, j = end+1; for (;;) Quicksort - partizione2: esempio di esecuzione 50 30 20 90 10 80 70 60 {pivot = a[start], i =start, j = end+1; for (;;) /* invariante: (a[start+1],…,a[i] <=pivot) && (a[j+1],…,a[end] >= pivot)*/ while ((a[++i] < pivot) && (i<=end)) ; while (a[--j] > pivot ; if (i > j) break; scambio(&a[j],&a[i]); } 0 1 2 3 4 5 6 7 i j 50 30 20 90 10 80 70 60 0 1 2 3 4 5 6 7 i j 50 30 20 10 90 80 70 60 0 1 2 3 4 5 6 7 i j

scambia(&a[start],&a[j]); j i Quicksort - partizione2: esempio di esecuzione if (i > j) break; 50 30 20 10 90 80 70 60 0 1 2 3 4 5 6 7 scambia(&a[start],&a[j]); j i 10 30 20 50 90 80 70 60 0 1 2 3 4 5 6 7 j qSort2(a,start, j-1); qSort2(a,j+1,end); 10 30 20 0 1 2 90 80 70 60 4 5 6 7