Una definizione naturale di mediana La mediana di una sequenza di elementi è un valore tale che, in una permutazione ordinata della sequenza, esso occupa il posto m+1-esimo, con m = n/2 (divisione intera). Nel caso di array indiciati a partire da 0 (come in C, Java, ecc.), la mediana è il valore dell'elemento di indice n/2 dopo che l'array è stato ordinato, cioè è un elemento che è preceduto da n/2 elementi minori o uguali, e seguito da n/2 (nel caso di n dispari) oppure n/2 – 1 elementi maggiori o uguali (nel caso di n pari). Nota: se n è dispari, n/2 = (n–1)/2; esempio: 7/2 = 3 = (7–1)/2; se n è pari, n/2 – 1 = (n–1)/2; esempio 8/2 – 1 = 3 = (8–1)/2; quindi: la mediana è un elemento "preceduto" da n/2 elementi minori o uguali, e "seguito" da (n–1)/2 elementi maggiori o uguali. Università di Torino – Facoltà di Scienze MFN Corso di Studi in Informatica Curriculum SR (Sistemi e Reti) Algoritmi e Laboratorio a.a. 2005-06 Lezioni prof. Elio Giovannetti Parte 9 – Ricerca della mediana. 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 Il problema astratto della ricerca della mediana Per sequenze di lunghezza dispari il mediano è l'elemento centrale nella permutazione ordinata della sequenza. Nelle sequenze di lunghezza gli elementi centrali nella permutazione ordinata sono due: il mediano, secondo la definizione precedente, è quello di destra. Esempio (consideriamo per semplicità una sequenza ordinata): 5, 7, 11, 28, 32, 45. La lunghezza della sequenza è 6. Secondo la definizione, il valore mediano è un valore per cui vi sono 6/2 = 3 elementi minori o uguali ad esso, e vi sono 5/2 = 2 elementi maggiori o uguali ad esso: quindi è 28. E. Giovannetti - AlgELab-05-06 - Lez.09 3 Controllo della soluzione (per array). E. Giovannetti - AlgELab-05-06 - Lez.09 Data una sequenza di n elementi a1, a2, ..., an, trovare il valore di un elemento ak tale che nella sequenza vi siano m elementi ak1, ak1, ..., akkm ≤ ak, con m = (n-1)/2 (div. intera) e con k1, k2, ..., km ≠ k, e tutti gli altri elementi siano ≥ ak L'importante è scegliere una definizione ed attenervisi. 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 4 Controllo della soluzione (visto in altro modo) Il valore x è mediano nell'array a di lunghezza n se nell'array esiste un elemento a[k] = x ed esistono n/2 elementi a[ j ] <= x, con indici j tutti distinti fra di loro e distinti da k, e tutti gli altri n – 1 – n/2 elementi sono ≥ ak Ossia: Il valore x è mediano nell'array a di lunghezza n se nell'array esistono (almeno) n/2 + 1 elementi <= x, con indici tutti distinti fra di loro, ed almeno n – 1 – n/2 (cioè (n-1)/2) elementi >= x static boolean isMedian(int x, int[] a) { int n = a.length; int countLess = 0; int countMore = 0; for(int i = 0; i < n; i++) if(a[i] <= x) countLess++; if(a[i] >= x) countMore++; return countLess > n/2 && countMore >= (n-1)/2; } 17/11/2005 Definizione alternativa. Naturalmente è altrettanto ragionevole, nel caso di sequenza di lunghezza pari, assumere come mediana l'elemento di "centro-sinistra" invece di quello di "centro-destra". La definizione di mediana allora diventa: Data una sequenza di n elementi a1, a2, ..., an, trovare il valore di un elemento ak tale che nella sequenza esistano m elementi ak1, ak1, ..., akkm ≤ ak, con m = n/2 (div. intera) e con k1, k2, ..., km ≠ k, e tutti gli altri (n – 1) /2 elementi siano ≥ ak 17/11/2005 2 5 La mediana deve bipartire l'array: se l' array fosse ordinato, la porzione contenente gli elementi uguali alla mediana contiene quindi l'elemento di indice n/2: < = > numMinori <= n/2 < numMinori + numUguali Se l'array non è ordinato, si possono contare separatamente i minori e gli uguali e poi verificare le disuguaglianze di cui sopra. Oppure, equivalentemente: numMinori <= n/2 && numMaggiori <= (n-1)/2 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 6 1 Il problema concreto L'idea. Dato un array di n elementi, indiciati da 0 a n-1, trovare la mediana della sequenza rappresentata dall'array. È permesso cambiare l'ordine degli elementi dell'array (ma ovviamente non i loro valori). La definizione fornisce direttamente un algoritmo banale per risolvere il problema. Riferendosi alla prima definizione: • si ordina l'array; • si prende l'elemento di indice n/2. (Ricorda che, essendo gli array indiciati a partire da 0, l'elemento di indice k è il k+1-esimo, cioè quello preceduto da k elementi). E. Giovannetti - AlgELab-05-06 - Lez.09 <= • se j > n/2, il mediano si trova sicuramente a sinistra di j; <= (il mediano sta qui) 7 17/11/2005 La situazione al generico passo 0 inf n-1 > a[sup] j sup ≤ a[inf] n-1 > a[sup] a[0..inf-1] ≤ a[inf..j-1] ≤ a[j] < a[j+1..sup] < a[sup+1..n-1] quindi la mediana ...: se j = n/2 : è a[j]; se j < n/2 : è in a[j+1..sup] perché in a[j+1..n-1] e in a[inf..sup]; se j > n/2 : è in a[inf..j-1] perché in a[0..j-1] e in a[inf..sup]; 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 8 E. Giovannetti - AlgELab-05-06 - Lez.09 0 inf j sup ≤ a[inf] n-1 > a[sup] a[0..j] ≤ a[j+1..sup] < a[sup+1..n-1] j+1 ≤ n/2 sup ≥ n/2 • v. ricorsiva: si richiama la procedura su a[j+1..sup]; • v. iterativa: si ripristina l'invariante eseguendo inf = j+1; a[0..inf-1] ≤ a[inf..sup] < a[sup+1..n-1] inf ≤ n/2 sup ≥ n/2 quindi la mediana si trova in a[inf..sup] Operando la partizione rispetto a un pivot si ottiene: inf > 1) se j < n/2 si ha: sup ≤ a[inf] 0 > (il mediano sta qui) • Si può "ripetere il procedimento" nella sottoporzione, e così via finché come pivot viene scelto il mediano (al peggio quando il sottoarray si riduce ad un solo elemento). Ma che cosa vuol dire "ripetere il procedimento" ? La complessità di tale soluzione è quindi n log n nel caso medio, ecc. Esistono però soluzioni migliori. 17/11/2005 • Eseguiamo sull'array la partizione del quicksort, rispetto ad un pivot scelto in qualche modo (ad esempio a caso). • Se, per massima fortuna, il pivot viene a trovarsi in posizione n/2, il mediano è proprio il pivot, e l'algoritmo è terminato. • Altrimenti, se j è la posizione finale del pivot: • se j < n/2, il mediano si trova sicuramente a destra di j; 9 2) se j > n/2 si ha: 0 inf j ≤ a[inf] sup n-1 > a[sup] a[0..inf-1] ≤ a[inf..j-1] < a[j..n-1] inf ≤ n/2 j-1 ≥ n/2 • v. ricorsiva: si richiama la procedura su a[inf..j-1]; • v. iterativa: si ripristina l'invariante eseguendo sup = j-1; 17/11/2005 Terminazione E. Giovannetti - AlgELab-05-06 - Lez.09 10 Complessità dei casi migliore e peggiore. Per l'invariante (o per la precondizione della chiamata ricorsiva) abbiamo sempre inf ≤ n/2 e sup ≥ n/2. D'altra parte ad ogni passo la lunghezza di a[inf..sup] diminuisce; quindi dopo un numero finito di passi, se non si è trovato prima un pivot che è il mediano, si ha: inf = sup = n/2 e il pivot banale di a[inf..sup], costituito dall'unico elemento, è il mediano. • caso migliore: il primo pivot è il mediano Tbest(n) = Θ(1); caso altamente improbabile e di scarso interesse; • caso peggiore: come nel quicksort, se tutte le partizioni risultano di sbilanciamento massimo, si ha: Tworst(n) = n + (n-1) + ...+ 2 + 1 = n(n+1)/2 = Θ(n2) esattamente come nel quicksort, con un opportuno procedimento di scelta del pivot (ad es. scelta casuale), anche tale caso diventa altamente improbabile e di scarso interesse; • caso medio: come nel quicksort, è il caso più interessante. L'algoritmo quindi termina sempre, e trova correttamente il mediano. 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 11 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 12 2 Complessità del caso medio: intuizione. Complessità del caso medio: dimostrazione. Mediamente il pivot si troverà non agli estremi di a[inf..sup], bensì non troppo lontano dalla metà. Pertanto la lunghezza di a[inf..sup] grosso modo si dimezza ad ogni successiva partizione. Poiché ogni volta la partizione della porzione a[inf..sup] richiede un numero di passi proporzionale alla sua lunghezza, si ha, assumendo per semplicità n = 2k : 17/11/2005 Adottando la stessa tecnica usata nel quicksort abbiamo: + ... + T(n) = n + n/2 + = 2k + 2k-1 + 2k-2 + ... + 1 = 2k+1 –1 = 2n - 1 n/22 quindi: L'espressione di T(n) è analoga a quella del caso medio del quicksort, con la differenza che qui per ognuna delle n possibili posizioni del pivot si ha la chiamata ricorsiva su una sola delle due parti, quindi: n/2k = Sostituendo: Tmedio(n) = Θ(n) E. Giovannetti - AlgELab-05-06 - Lez.09 13 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 14 Compl. del caso medio: dimostrazione (continua) Abbiamo così ottenuto le equazioni di ricorrenza: T(0) = 0; T(n) < T(n-1) + 2 Espandendo, con T(n-1) < T(n-2) + 2, T(n-2) < T(n-3) + 2, ecc. si ottiene: T(n) < T(n-2) + 2⋅2 < T(n-3) + 2⋅3 ... < 2⋅n Analogamente si ha T(n) ≥ T(n-1) + 1, quindi T(n) ≥ n Tmedio(n) = Θ(n) 17/11/2005 E. Giovannetti - AlgELab-05-06 - Lez.09 15 3