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 ).