Algoritmi e Laboratorio aa 2005-06 Lezioni Una definizione naturale

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