Logo dell'Università di Catania: Siciliae Studium Generale 1434 Logo del Centro IPPARI, Programmazione
matite e gomma
Loghi istituzionali: Siciliae Studium Generale 1434, Centro ricerche IPPARI, Università di Catania, Facoltà di Scienze Matematiche, Fisiche, Naturali, Insegnamento di Programmazione

Ricorrenze e stima delle prestazioni

nell'analisi degli algoritmi

Lezione 5 di Programmazione 2

Docente: Giuseppe Scollo

Università di Catania, sede di Comiso (RG)
Facoltà di Scienze Matematiche, Fisiche e Naturali
Corso di Studi in Informatica applicata, AA 2006-7

Logo di Conformità WCAG-1 di Livello Tripla A, W3C-WAI Web Content Accessibility Guidelines 1.0 Validazione XHTML 1.0 Validazione CSS 2

Indice

  1. Ricorrenze e stima delle prestazioni
  2. relazioni di ricorrenza
  3. C(N) = C(N-1) + N, per N > 1, C(1)=1
  4. C(N) = C(N/2) + 1, per N > 1, C(1)=1
  5. C(N) = C(N/2) + N, per N > 1, C(1)=0
  6. C(N) = 2C(N/2) + N, per N > 1, C(1)=0
  7. C(N) = 2C(N/2) + 1, per N > 1, C(1)=1
  8. ricerca sequenziale
  9. prestazioni della ricerca sequenziale
  10. ricerca binaria
  11. complessità di algoritmi e di problemi
  12. esercizi

relazioni di ricorrenza

molti algoritmi, progettati seconda la regola divide et impera, risolvono il problema decomponendolo in istanze più piccole dello stesso problema

più precisamente, la soluzione per un valore N del parametro primario (dimensione dell'input o di una struttura dati fondamentale dell'algoritmo) è espressa in termini della stessa soluzione per un valore minore di tale parametro

l'analisi delle prestazioni di tali algoritmi si basa sulla soluzione di relazioni di ricorrenza, nelle quali il costo computazionale CN è definito induttivamente, a partire da un costo di base C1, per il tramite di un'equazione ricorsiva

mostriamo nel seguito una semplice tecnica di soluzione di ricorrenze attraverso sostituzioni successive per alcune classi fondamentali di algoritmi

C(N) = C(N-1) + N, per N > 1, C(1)=1

classe degli algoritmi:   si esamina tutto l'input e se ne elimina un elemento, reiterando quindi l'algoritmo sul resto dell'input

ricorrenza:   CN = CN-1 + N,   per N > 1 , con C1 = 1

soluzione:   CN

C(N) = C(N/2) + 1, per N > 1, C(1)=1

classe degli algoritmi:   si dimezza l'input ad ogni passo, di costo costante, reiterando quindi l'algoritmo sull'input dimezzato

ricorrenza:   CN = CN/2 + 1,   per N > 1 , con C1 = 1

soluzione:   per N = 2n si ha:   C2n

e più in generale   CN lg N   se si interpreta il dimezzamento come quoziente intero, N/2 = N/2, poiché in tal caso il numero di iterazioni è uguale al numero di bit necessari alla rappresentazione binaria naturale di N, cioè lg N

C(N) = C(N/2) + N, per N > 1, C(1)=0

classe degli algoritmi:   ad ogni passo si esamina tutto l'input e lo si dimezza, reiterando quindi l'algoritmo sull'input dimezzato

ricorrenza:   CN = CN/2 + N,   per N > 1 , con C1 = 0

soluzione:   se si adopera il quoziente intero, N/2k = N/2k, si ha:   CN

dove il fattore 2 è il limite della serie geometrica di ragione 1/2, e l'errore di tale approssimazione per eccesso tende a 0 per N

C(N) = 2C(N/2) + N, per N > 1, C(1)=0

classe degli algoritmi:   ad ogni passo si esamina tutto l'input e lo si divide in due metà, reiterando quindi l'algoritmo su entrambe le metà

ricorrenza:   CN = 2CN/2 + N,   per N > 1 , con C1 = 0

soluzione:   nell'ipotesi che N = 2n, si ha:   C2n = 2C2n-1 + 2n

quindi, dividendo entrambi i membri per 2n:   C2n/2n

dunque CN = N lg N nell'ipotesi posta, e più in generale CN N lg N

C(N) = 2C(N/2) + 1, per N > 1, C(1)=1

classe degli algoritmi:   ad ogni passo, di costo costante, si divide l'input in due metà, reiterando quindi l'algoritmo su entrambe le metà

ricorrenza:   CN = 2CN/2 + 1,   per N > 1 , con C1 = 1

soluzione:   nell'ipotesi che N = 2n, si ha:   C2n = 2C2n-1 + 1

quindi, dividendo entrambi i membri per 2n:   C2n/2n

approssimando per eccesso con il limite della serie geometrica di ragione 1/2

e quindi, più generalmente per qualsiasi N :   CN 2N

ricerca sequenziale

a titolo di esempio, vediamo l'uso di relazioni di ricorrenza nel confronto delle prestazioni di algoritmi diversi per uno stesso problema:

un semplice algoritmo per il problema in esame effettua la scansione sequenziale degli elementi nell'array, confrontandoli con l'elemento dato fino a quando

prestazioni della ricerca sequenziale

ecco una codifica di questo algoritmo come funzione C++, dove i parametri l, r sono indici che delimitano lo spazio di ricerca del valore v nell'array a, dunque N = r-l+1, e si ha il valore di ritorno -1 quando la ricerca ha esito negativo:

int search(int a[], int v, int l, int r)
  { 
    for (int i = l; i <= r; i++)
      if (v == a[i]) return i;
    return -1; 
  }

ricerca binaria

l'ordinamento preventivo dell'array, che produce un modesto guadagno prestazionale nella ricerca sequenziale, è sfruttato molto meglio nell'algoritmo di ricerca binaria, che ad ogni passo con esito negativo dimezza la dimensione dello spazio di ricerca nel passo successivo

ecco una codifica dell'algoritmo come funzione C++:

int search(int a[], int v, int l, int r)
  {
    while (r >= l)
      { int m = (l+r)/2;
        if (v == a[m]) return m;
        if (v < a[m]) r = m-1; else l=m+1;
      }
    return -1;
  }

dallo studio già effettuato della ricorrenza CN = CN/2 + 1 possiamo concludere che il costo della ricerca binaria è proporzionale a lg N (anche nel caso peggiore)

il guadagno di efficienza rispetto alla ricerca sequenziale è esponenziale!

complessità di algoritmi e di problemi

finora si sono considerate stime e analisi delle prestazioni di algoritmi per un dato problema, o classe di problemi, e per essi risulta utile distinguere fra

per complessità computazionale di un problema si intende:

il costo computazionale nel caso peggiore di un algoritmo per un dato problema costituisce un limite superiore alla complessità computazionale del problema:

è anche utile determinare limiti inferiori alla complessità computazionale di problemi, analizzandone caratteristiche inerenti, ma questo compito è di solito più difficile

esercizi

  1. mostrare che la soluzione della ricorrenza CN = CN/2 + 1 è lg N   anche con l'approssimazione intera per eccesso del dimezzamento: N/2 = N/2
  2. mostrare che la soluzione della ricorrenza CN = CN/2 + N è 2N   anche con l'approssimazione intera per eccesso del quoziente: N/2k = N/2k
  3. risolvere la ricorrenza CN = kCN/2   per N > 1 , con C1 = 1, per il caso in cui N = 2n
  4. modificare la codifica C++ della funzione di ricerca sequenziale, assumendo che l'array a sia ordinato per valori crescenti, in modo che la ricerca termini con esito negativo quando il valore dell'elemento dell'array è maggiore di quello cercato