Algoritmi e Strutture di Dati Complessità degli algoritmi m.patrigai Nota di copyright queste slides soo protette dalle leggi sul copyright il titolo ed il copyright relativi alle slides (iclusi, ma o limitatamete, immagii, foto, aimazioi, video, audio, musica e testo) soo di proprietà degli autori idicati sulla prima pagia le slides possoo essere riprodotte ed utilizzate liberamete, o a fii di lucro, da uiversità e scuole pubbliche e da istituti pubblici di ricerca ogi altro uso o riproduzioe è vietata, se o esplicitamete autorizzata per iscritto, a priori, da parte degli autori gli autori o si assumoo essua resposabilità per il coteuto delle slides, che soo comuque soggette a cambiameto questa ota di copyright o deve essere mai rimossa e deve essere riportata ache i casi di uso parziale
Algoritmi e programmi u algoritmo coicide per oi co la sua descrizioe i pseudocodice sappiamo che ciò è equivalete a defiire ua Radom Access Machie lo pseudocodice può essere facilmete tradotto i u liguaggio di programmazioe arbitrario per otteere u programma l operazioe di traduzioe di u algoritmo i u programma viee detta implemetazioe Esecuzioe dei programmi il programma può essere eseguito su ua piattaforma qualsiasi co dati di iput qualsiasi la sua esecuzioe ha u costo (ecoomico) che può essere espresso tramite le risorse di calcolo utilizzate tempo memoria traffico geerato su rete trasferimeto dati da/su disco... ella maggior parte dei casi la risorsa più critica è il tempo di calcolo 2
Tempo di calcolo e dimesioe dell iput si riscotra che il tempo di calcolo cresce al crescere della dimesioe dell iput è legittimo misurarlo come ua fuzioe di la ozioe di dimesioe dell iput dipede dal problema ordiameto: umero di elemeti da ordiare operazioi su liste: lughezza della lista operazioi su matrici: dimesioe massima delle matrici coivolte valutazioe di u poliomio i u puto: grado del poliomio Fattori che ifluezao il tempo di calcolo dimesioe dell iput maggiore è la quatità di dati i iput maggiore è il tempo ecessario per processarli algoritmo può essere più o meo efficiete hardware u supercalcolatore è più veloce di u persoal computer liguaggio u implemetazioe diretta i liguaggio macchia è più veloce di u implemetazioe i u liguaggio ad alto livello u programma compilato è più veloce di u programma iterpretato compilatore alcui compilatori soo progettati per geerare codice efficiete programmatore a parità di algoritmo e di liguaggio, programmatori esperti scelgoo costrutti più veloci 3
Tempo di calcolo e algoritmi esperimeto: cofroto tra due algoritmi di ordiameto stesso iput:.000.000 umeri iteri algoritmo hardware liguaggio compilatore programmatore tempo isertio sort supercalcolatore liguaggio macchia esperto 5,56 ore merge sort persoal computer liguaggio ad alto livello o efficiete medio 6,67 miuti coclusioe: il tempo di calcolo di u programma su uo specifico iput è ifluezato dall algoritmo che implemeta più che dagli altri fattori Progetto di algoritmi efficieti motivazioe ha u impatto ecoomico diretto per gli utilizzatori dei programmi il tempo di calcolo si traduce i u ivestimeto ecoomico è fodametale per otteere implemetazioi di uso pratico l usabilità di u programma può essere compromessa da u algoritmo iefficiete problema el mometo i cui progettiamo u algoritmo o possiamo misurare direttamete l efficieza delle sue future implemetazioi soluzioe previsioe del tempo di calcolo delle implemetazioi di u algoritmo (aalisi) 4
obiettivo Aalisi degli algoritmi prevedere il tempo di calcolo richiesto dall esecuzioe di u programma che implemeta il ostro algoritmo i fuzioe della dimesioe dell iput per piccoli iput il tempo di calcolo sarà comuque basso qual è il tempo di calcolo per iput di gradi dimesioi? strumeti ipotesi sul tempo di esecuzioe di ogi istruzioe aalisi asitotica delle fuzioi quale fuzioe cosideriamo? Il tempo di calcolo o è ua fuzioe I geerale il tempo di calcolo per u iput di dimesioe o è ua fuzioe T() el caso peggiore T() el caso medio T() el caso migliore =5 =0 =5 =20 =25 =30 5
Tempo di calcolo e aalisi asitotica vogliamo studiare il tempo di calcolo co gli strumeti dell aalisi asitotica ma l aalisi asitotica si applica solo alle fuzioi dobbiamo trasformare il tempo di calcolo i ua fuzioe per questo cosideriamo il caso peggiore/medio/migliore fuzioi caso peggiore caso medio caso migliore O Ω Θ Uso del caso peggiore i geerale è di maggiore iteresse il caso peggiore rispetto al caso migliore o al caso medio si preferisce u errore per eccesso ad u errore per difetto il caso migliore o dà essua garazia sul tempo di calcolo co u iput geerico è di iteresse solamete teorico spesso il tempo di calcolo del caso medio è più vicio al caso peggiore che al caso migliore cooscere il costo del caso medio può essere utile solo qualora si debba ripetere u operazioe u umero elevato di volte 6
Stima del tempo di calcolo deotiamo T() il tempo di calcolo di ua implemetazioe dell algoritmo el caso peggiore su u iput di dimesioe vogliamo stimare T() a partire dallo pseudocodice quato costa ogi operazioe elemetare? ipotesi semplificativa: per eseguire ua liea (o istruzioe) di pseudocodice è richiesto tempo costate deotiamo co c i il tempo ecessario per eseguire la riga i Strategie per la stima del tempo di calcolo strategia più oerosa calcoliamo esplicitamete T() a partire dallo pseudocodice T() dipede, oltre dalla dimesioe dell iput, ache dal costo di esecuzioe associato alle sigole righe dello pseudocodice c, c 2, c 3, c 4, studiamo il comportameto asitotico di T() strategia più veloce calcoliamo il costo asitotico di ogi porzioe dello pseudocodice otteiamo il costo asitotico dell itero algoritmo compoedo i costi calcolati otteiamo il comportamete asitotico di T() seza mai calcolare esplicitamete T() 7
Esempio di algoritmo algoritmo DUE_OCCORRENZE accetta i iput u array di iteri ritora TRUE se esiste u valore che occorre almeo due volte ritora FALSE se tutti gli elemeti soo diversi DUE_OCCORRENZE(A). output = false 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true 6. retur output Esempio di calcolo di T() DUE_OCCORRENZE(A). output = false 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true 6. retur output costo c c 2 c 3 c 4 c 5 c 6 di volte suppoiamo che ogi riga i abbia u costo di esecuzioe c i cotiamo quate volte ogi riga viee eseguita 8
Esempio di calcolo di T() DUE_OCCORRENZE(A). output = false 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true 6. retur output costo c c 2 c 3 c 4 c 5 c 6 di volte A = 0 i=2 =8 A.legth-=7 il ciclo estero viee eseguito eseguito - volte il test, duque, viee eseguito volte l ultimo test determia l uscita dal ciclo for il ciclo itero viee eseguito -i- volte DUE_OCCORRENZE(A). output = false 6. retur output Esempio di calcolo di T() 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true per ogi i, il ciclo itero viee eseguito -i- volte, cioè: Σ i=0..-2 (-i-) = Σ i=0..-2 () - Σ i=0..-2 (i) - Σ i=0..-2 () dove Σ i=0..-2 () = (-) formula di di Gauss Σ i=0..-2 (i) = (-)(-2)/2 Σ i=.. (i) = (+) Σ i=0..-2 () = (-) costo c c 2 c 3 c 4 c 5 c 6 di volte Σ i=.. (i) = (+) 2 9
DUE_OCCORRENZE(A). output = false 6. retur output Esempio di calcolo di T() 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true costo il ciclo itero viee eseguito (-) - (-)(-2)/2 - (-) = 2 - - ( 2-3+2)/2 - + = 2 - -0.5 2 +.5- ++ = 0.5 2 +.5 volte il test alla riga 2 viee eseguito 0.5 2 +.5+ volte c c 2 c 3 c 4 c 5 c 6 di volte 0.5 2 +.5+ 0.5 2 +.5 0.5 2 +.5 Esempio di calcolo di T() DUE_OCCORRENZE(A). output = false 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true 6. retur output costo c c 2 c 3 c 4 c 5 c 6 di volte 0.5 2 +.5+ 0.5 2 +.5 0.5 2 +.5 T() = c +c 2 ()+c 3 (0.5 2 +.5+)+(c 4 +c 5 )(0.5 2 +.5)+c 6 = 2 (0.5c 3 +0.5c 4 +0.5c 5 ) + (c 2 +.5c 3 +.5c 4 +.5c 5 ) + (c +c 3 +c 6 ) Duque: T() O( 2 ), T() Ω( 2 ) T() Θ( 2 ) 0
Algoritmi e complessità O(f()) sia T() il tempo di esecuzioe di u algoritmo A su u istaza di dimesioe el caso peggiore l algoritmo A ha complessità temporale O(f()) se T() = O(f()) diciamo ache che il tempo di esecuzioe dell algoritmo A è al più f() f() è u limite superiore, o upper-boud, al tempo di esecuzioe dell algoritmo A f() è la quatità di tempo sufficiete (i ogi caso) all esecuzioe dell algoritmo A Algoritmi e complessità O(f()) l algoritmo A ha complessità temporale O(f()) se T() = O(f()) c f() T() (caso peggiore)
Sigificatività della fuzioe f() se u algoritmo ha complessità f() allora ha ache complessità g() per ogi g() tale che f() O(g()) la complessità espressa tramite la otazioe O-grade diveta tato più sigificativa quato più f() è strigete (piccolo) c g() c f() T() (caso peggiore) Algoritmi e complessità Ω(f()) sia T() il tempo di esecuzioe di u algoritmo A su u istaza di dimesioe el caso peggiore l algoritmo A ha complessità temporale Ω(f()) se T() = Ω(f()) diciamo ache che il tempo di esecuzioe dell algoritmo A è almeo f() f() è u limite iferiore, o lower-boud, al tempo di esecuzioe dell algoritmo A f() è la quatità di tempo ecessaria (i almeo u caso) all esecuzioe dell algoritmo A 2
Algoritmi e complessità Ω(f()) l algoritmo A ha complessità temporale Ω(f()) se T() = Ω(f()) caso peggiore c f() N Algoritmi e complessità Θ(f()) sia T() il tempo di esecuzioe di u algoritmo A su u istaza di dimesioe el caso peggiore l algoritmo A ha complessità temporale Θ(f()) se ha complessità temporale O(f()) e Ω(f()) diciamo ache che il tempo di esecuzioe dell algoritmo è f() f() è u limite iferiore e superiore (lower-boud e upperboud), al tempo di esecuzioe dell algoritmo f() è la quatità di tempo ecessaria e sufficiete all esecuzioe dell algoritmo 3
Algoritmi e complessità Θ(f()) l algoritmo A ha complessità temporale Θ(f()) se T() = Θ(f()) c f() caso peggiore c 2 f() N Strategie di aalisi più efficieti DUE_OCCORRENZE(A). output = false 2. for i = 0 to A.legth-2 3. for j = i+ to A.legth- 4. if (A[i] == A[j]) 5. output = true 6. retur output di volte 0.5 2 +.5+ 0.5 2 +.5 0.5 2 +.5 la regola della somma mi assicura che l adameto asitotico di ua somma di termii può essere otteuto cosiderado l adameto asitotico dei sigoli termii ua strategia più efficiete si basa, duque, sul calcolo diretto del costo asitotico di ogi riga ordie Θ() Θ() Θ( 2 ) Θ( 2 ) Θ( 2 ) Θ() 4
Calcolo efficiete del costo asitotico istruzioi semplici tempo di esecuzioe costate: Θ() sequeza (ecessariamete fiita) di istruzioi semplici tempo di esecuzioe costate: Θ() sequeza di istruzioi geeriche somma dei tempi di esecuzioe di ciascua istruzioe Istruzioi codizioali. if <codizioe> the per calcolare T() 2. <parte-the> occorrerebbe sapere 3. else se la codizioe si 4. <parte-else> verifica o meo troviamo u limite superiore O-grade al tempo di esecuzioe T() come somma dei costi segueti costo O-grade della valutazioe della codizioe costo O-grade maggiore tra <parte-the> e <parteelse> troviamo u limite iferiore Ω al tempo di esecuzioe T() come somma dei costi segueti costo Ω della valutazioe della codizioe costo Ω miore tra <parte-the> e <parte-else> 5
Istruzioi ripetitive il ostro pseudocodice ci offre tre istruzioi ripetitive for, while e repeat per il limite superiore O-grade occorre determiare u limite superiore O(f()) al umero di iterazioi del ciclo u limite superiore O(g()) al tempo di esecuzioe di ogi iterazioe si compoe del costo dell esecuzioe del blocco di istruzioi più il costo di esecuzioe del test il costo del ciclo sarà: O(g() f()) aalogamete sarà: Ω(g () f ()) dove le iterazioi soo Ω(g ()) ed il costo di ua iterazioe è Ω(f ()) FACT(). f = 2. for k = 2 to 3. f = f * k 4. retur f Istruzioi ripetitive: esempio umero di iterazioi del ciclo for: Θ() costo di ua sigola iterazioe: Θ() costo complessivo del ciclo while: Θ( ) = Θ() costo complessivo della procedura: Θ() 6
FACT(). f = 2. for k = 2 to 3. f = f * k 4. retur f Attezioe al modello! stiamo lavorado ell ipotesi i cui le variabili (che corrispodoo ai registri della RAM) riescao sempre a coteere i umeri coivolti se usassimo come misura dell iput il umero k di bit ecessari per rappresetare i biario avremmo k = log 2 e costo complessivo = Θ(2 k ) Chiamata a fuzioe o procedura. P( ) suppoiamo che u 2. programma P ivochi la 3. Q( ) procedura Q 4. sia T Q () il tempo di esecuzioe della procedura Q il tempo di esecuzioe dell ivocazioe della procedura Q i P è T Q (m), dove m è la dimesioe dell iput passato alla procedura Q attezioe: occorre determiare la relazioe tra m e la dimesioe dell iput di P 7
Esempio di chiamata a fuzioe SUM_OF_FACT(). sum = 0 2. for m = 0 to 3. sum = sum + FACT(m) 4. retur sum il corpo del ciclo for ha complessità Θ() + Θ(m) = Θ(m) il ciclo viee eseguito volte il costo complessivo del ciclo è duque: Θ() + Θ(-) +... + Θ(2) + Θ() = Θ( 2 ) il costo totale è Θ( 2 ) Ua giustificazioe grafica (-) + (-2) + (-3) +... + 3 + 2 + /2 = 2 /2 = Θ( 2 ) 8
Esempio di aalisi della complessità algoritmo per ivertire u array 0 2 3 4 5 6 7 7 6 5 4 3 2 0 da a strategia scambio A[0] co A[A.legth-] scambio A[] co A[A.legth-2] scambio A[2] co A[A.legth-3] scambio A[3] co A[A.legth-4]... 7 2 3 4 5 6 0 7 6 2 3 4 5 0 7 6 5 3 4 2 0 7 6 5 4 3 2 0 Esempio di aalisi della complessità INVERTI_ARRAY(A). for i = 0 to A.legth/2 do 2. SCAMBIA(A,i,A.legth--i) SCAMBIA(A,j,k). memo = A[j] 2. A[j] = A[k] 3. A[k]= memo la fuzioe SCAMBIA(A,j,k) ha complessità Θ() i quato è composta da ua successioe di istruzioi elemetari la fuzioe INVERTI_ARRAY(A) ha complessità Θ() i quato esegue per Θ() volte il blocco delle istruzioi che cosiste ell esecuzioe di ua procedura Θ() 9