Capitolo 3
Divide et impera
3.1 Dato un array ordinato contenente n elementi di tipo intero. Progettare un algoritmo che data una chiave k restituisce il numero di occorrenze di k nell’array.
L’algoritmo deve avere complessità O(log n)
3.2 Modificare il codice ricorsivo della ricerca binaria in modo che, nel caso che
l’array a memorizzi un multi-insieme ordinato dove gli elementi possono comparire più volte, restituisca la posizione più a destra dell’elemento cercato.
3.5 Data la seguente funzione ricorsiva foo, trovate la corrispondente relazione di
ricorrenza e risolvetela utilizzando il teorema principale.
foo( n ){
if (n < 10) {
return 1;
} else if (n < 1234){
tmp = n;
for (i = 1; i <= n; i = i+1)
for (j = 1; j <= n; j = j+1)
for (k = 1; k <= n; k = k+1)
tmp = tmp + i * j * k;
} else {
tmp = n;
for (i = 1; i <= n; i = i+1)
for (j = 1; j <= n; j = j+1)
tmp = tmp + i * j;
}
return tmp + foo( n/2 ) * foo( n/2 ) + foo( n/2 );
}
PA
10
Capitolo 3 – Divide et impera
Soluzioni
3.1 La soluzione è una variante della ricerca binaria: deve evitare di effettuare
chiamate ricorsive su segmenti S dell’array che (a) contengono chiavi k e (b)
l’elemento nell’array che precede S e quello che succede a S nell’array sono
uguali a k.
conta( a, sx, dx, k):
if (sx > dx) return 0;
cx = (sx+dx)/2;
if (k < a[cx])
return conta(a, sx, cx-1);
else if (k > a[cx])
return conta(a, cx+1, dx);
else {
c = 1;
if (k == a[sx])
c = c + (cx-sx);
else
c = c + conta(a, sx, cx-1);
if (k == a[dx])
c = c + (dx-cx);
else
c = c + conta(a, cx+1, dx);
return c;
}
3.2 L’osservazione da fare è che l’operazione centro = (sinistra+destra)/2
tronca e, quindi, occorre arrotondare con centro = (sinistra+destra+1)/2:
a questo punto soltanto il codice simmetrico a quello della ricerca binaria può
funzionare (altrimenti va in loop!).
PA
Programmare algoritmi
1 RicercaBinariaRicorsiva( a, k, sinistra, destra ):
2
�pre: a ordinato e 0 � sinistra � destra � n − 1�
3
if (sinistra == destra) {
4
if (k == a[sinistra]) {
5
return sinistra;
6
} else {
7
return -1;
8
}
9
}
10
centro = (sinistra+destra+1)/2;
11
if (k >= a[centro]) {
12
return RicercaBinariaRicorsiva( a, k, centro, destra );
13
} else {
14
return RicercaBinariaRicorsiva( a, k, sinistra,
centro-1 );
15
}
3.5 Data la seguente funzione ricorsiva foo, trovate la corrispondente relazione di
ricorrenza e risolvetela utilizzando il teorema principale.
foo( n ){
if (n < 10) {
return 1;
} else if (n < 1234){
tmp = n;
for (i = 1; i <= n; i = i+1)
for (j = 1; j <= n; j = j+1)
for (k = 1; k <= n; k = k+1)
tmp = tmp + i * j * k;
} else {
tmp = n;
for (i = 1; i <= n; i = i+1)
for (j = 1; j <= n; j = j+1)
tmp = tmp + i * j;
}
return tmp + foo( n/2 ) * foo( n/2 ) + foo( n/2 );
}
Sia T (n) il costo della funzione foo. La relazione di ricorrenza ha come caso
base di costo costante il ramo then e il primo ramo else. Il secondo ramo then
è quello che contribuisce realmente al costo asintotico
T (n) � 3T (n/2) + cn2 .
11
PA
12
Capitolo 3 – Divide et impera
Per la sua soluzione con il teorema fondamentale, osserviamo che α = 3, β =
2, and f(n) = n2 : esiste γ < 1 tale che αf(n/β) = γf(n); infatti, 3(n/2)2 =
(3/4)n2 e quindi γ = 3/4. Quindi rientriamo nel primo caso del teorema e
T (n) = O(f(n)) = O(n2 ).