La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

COMPLESSITÀ DEGLI ALGORITMI Tra i problemi che ammettono soluzione ne esistono di più facili e di più difficili. Teoria della complessità (anni 70): complessità

Presentazioni simili


Presentazione sul tema: "COMPLESSITÀ DEGLI ALGORITMI Tra i problemi che ammettono soluzione ne esistono di più facili e di più difficili. Teoria della complessità (anni 70): complessità"— Transcript della presentazione:

1 COMPLESSITÀ DEGLI ALGORITMI Tra i problemi che ammettono soluzione ne esistono di più facili e di più difficili. Teoria della complessità (anni 70): complessità di un problema complessità di un programma valutazione dellefficienza di un algoritmo Un programma richiede spazio di memoria e tempo di calcolo. Per valutare la complessità dei programmi, ci concentreremo sul secondo aspetto.

2 COMPLESSITÀ DI UN ALGORITMO Come valutare la complessità di uno specifico algoritmo? Contando il numero di operazioni aritmetiche, logiche, di accesso ai file, etc. Ipotesi semplificative: ogni operazione abbia costo unitario il tempo globalmente impiegato sia proporzionale al numero di operazioni eseguite. Non ci si riferisce ad una specifica macchina.

3 ESEMPIO Per moltiplicare due matrici quadrate NxN di interi (C = A x B), occorre: ripetere N 2 volte il calcolo del valore C[i,j] per calcolare C[i,j], effettuare 2N letture, N moltiplicazioni, N-1 addizioni e 1 scrittura Totale: 2N 3 letture, N 3 moltiplicazioni, N 2 *(N-1) addizioni, N 2 scritture Tempo richiesto: (ipotesi: stesso tempo per tutte le operazioni): time Alg( C = AxB ) (N) = 2N 3 +N 3 +N 2 (N-1)+N 2 = 4N 3

4 MOTIVAZIONI Perché valutare la complessità di un algoritmo? per sceglierelalgoritmo più efficiente Da cosa dipende la complessità di un algoritmo? dallalgoritmo stesso (ovvio...) dalla dimensione dei dati a cui lalgoritmo si applica. La complessità dellalgoritmo viene dunque espressa in funzione della dimensione dei dati.

5 MOTIVAZIONI Si consideri un algoritmo che risolve il generico problema P. Avere time Alg(P) (N) = 2 N è molto diverso da avere time Alg(P) (N) = 4*N 3 perché cambia lordine di grandezza del problema.

6 ORDINI DI GRANDEZZA Tanto per quantificare: Se un elaboratore esegue 1000 operazioni/sec, un algoritmo il cui tempo sia dellordine di 2 N richiede:

7 COMPORTAMENTO ASINTOTICO Problema: individuare con esattezza lespressione di time A (N) è spesso molto difficile Daltronde, interessa capire cosa succede quando i dati sono di grandi dimensioni con N piccolo, in pratica, qualunque algoritmo è OK è con N grande che la situazione può diventare critica ( in particolare: per N ) Per questo ci interessa il comportamento asintotico della funzione time A (N).

8 COMPORTAMENTO ASINTOTICO Anche individuare il comportamento asinto- tico di time A (N) non è però sempre semplice Daltronde, interessa non tanto lespressio- ne esatta, quanto lordine di grandezza costante al variare di N lineare, quadratico... (polinomiale) al variare di N logaritmico al variare di N esponenziale al variare di N Si usano notazioni che danno unidea del comportamento asintotico della funzione.

9 NOTAZIONI ASINTOTICHE Limite superiore al comportamento asintotico di una funzione (notazione O) quando esistono tre costanti a, b, N tali che time (N) N e si scrive time(N) = O (g(N)) Limite inferiore al comportamento asintotico di una funzione (notazione ) quando esistono due costanti c, N tali che time (N) > c f(N) N > N e si scrive time(N) = (f(N))

10 NOTAZIONI ASINTOTICHE Limite superiore al comportamento asintotico di una funzione (notazione O) quando esistono tre costanti a, b, N tali che time (N) N e si scrive time(N) = O (g(N)) Limite inferiore al comportamento asintotico di una funzione (notazione ) quando esistono due costanti c, N tali che time (N) > c f(N) N > N e si scrive time(N) = (f(N)) La funzione g(N) costituisce un limite superiore al costo dellalgoritmo (time(N)). La funzione f(N) costituisce un limite inferiore al costo dellalgoritmo (time(N)).

11 NOTAZIONI ASINTOTICHE Magari non sapremo esattamente come è fatta time(N) per N..... però sappiamo che è non meno di f(N)......e non più di g(N).

12 COMPORTAMENTO ASINTOTICO Caso particolare: se esiste una funzione f(N) tale che time A (N) = O (f(N)) = (f(N)) allora f(N) costituisce una valutazione esatta del costo dellalgoritmo. In questo caso, infatti, le due delimitazioni infe- riore e superiore coincidono, e dunque caratte- rizzano compiutamente time(N).

13 ESEMPIO Si supponga che per un certo algoritmo sia time A (N) = 3*N 2 + 4*N + 3 Poiché 3*N 2 + 4*N + 3 4*N 2 N>3, si può dire che time A (N) = O(N 2 ) Daltronde, 3*N 2 + 4*N + 3 > 3*N 2 N>1, e quindi time A (N) = (N 2 ) La funzione f(N)= N 2 è perciò una valutazione esatta del costo di questo algoritmo.

14 CLASSI DI COMPLESSITÀ Le notazioni O e consentono di dividere gli algoritmi in classi, in base allordine di grandezza della loro complessità. costante1,... k,... sotto-linearelog N oppure N k con k<1 lineareN sovra-lineareN*log N, e N k con k>1 esponenzialec N oppure N N Obiettivo: dati due algoritmi, capire se sono della stessa complessità o se uno è migliore (più effi- ciente, meno complesso) dellaltro.

15 ALGORITMO MIGLIORE Dati due algoritmi A1 e A2 che risolvono lo stesso problema P, A1 è migliore di A2 nel risolvere il problema P se: time A1 (N) èO(time A2 (N)) time A2 (N)non èO(time A1 (N)) Ad esempio, se per due algoritmi A e B risulta: time A (N) = 3 N 2 + N time B (N) = N log N lalgoritmo B è migliore di A.

16 COMPLESSITÀ DI UN PROBLEMA Finora ci siamo interessati della complessità del singolo algoritmo che risolve un certo problema. Ora interessa capire se il problema in quanto tale abbia una sua complessità, cioè se sia intrinsecamente facile o intrinsecamente difficile indipendente- mente dallalgoritmo che possiamo inventare per risolverlo.

17 COMPLESSITÀ DI UN PROBLEMA Diremo allora che un problema ha: delimitazione superiore O(g(N)) alla sua complessità se esiste ALMENO UN algorit- mo che lo risolve con complessità O(g(N)) delimitazione inferiore (f(N)) alla sua complessità se OGNI algoritmo che lo risolve è di complessità ALMENO (f(N)).

18 COMPLESSITÀ DI UN PROBLEMA Diremo allora che un problema ha: delimitazione superiore O(g(N)) alla sua complessità se esiste ALMENO UN algorit- mo che lo risolve con complessità O(g(N)) delimitazione inferiore (f(N)) alla sua complessità se OGNI algoritmo che lo risolve è di complessità ALMENO (f(N)). Il problema non può essere più complesso di O(g(N)), perché almeno un algoritmo che lo risolve con tale complessità esiste. Però potrebbe essere più semplice (possiamo essere noi a non aver trovato lalgoritmo migliore). Per dire che il problema è sicuramente di com- plessità (f(N)) bisogna dimostrare che non può esistere un algoritmo migliore, ossia che qualunque altro algoritmo che possiamo inventare avrà comunque almeno quella complessità.

19 CLASSI DI PROBLEMI Diremo che un problema ha complessità: lineare, se ogni algoritmo che lo risolve ha delimitazioni di complessità O(N) e (N) polinomiale, se ogni algoritmo risolvente ha delimitazioni di complessità O(N k ) e (N k ) Problema intrattabile: un problema per cui non esistono algoritmi risolventi di complessità polinomiale (esempio: commesso viaggiatore).

20 ALGORITMI OTTIMALI Diremo che un algoritmo è ottimale se lalgoritmo stesso ha complessità O(f(N)) la delimitazione inferiore alla complessità del problema è (f(N)) È piuttosto ovvio: se il problema in quanto tale ha complessità (f(N)), e lalgoritmo in questio- ne ha appunto complessità (f(N)), di meglio non potremo mai trovare.

21 VALUTAZIONI DI COMPLESSITÀ Come valutare la complessità in pratica ? Concetto di ISTRUZIONE DOMINANTE Dato un algoritmo A il cui costo è t(N), una sua istruzione viene detta dominante se esistono opportune costanti a, b, N tali che t(N) N dove d(N) indica quante volte listruzione dominante viene eseguita.

22 VALUTAZIONI DI COMPLESSITÀ Come valutare la complessità in pratica ? Concetto di ISTRUZIONE DOMINANTE Dato un algoritmo A il cui costo è t(N), una sua istruzione viene detta dominante se esistono opportune costanti a, b, N tali che t(N) N dove d(N) indica quante volte listruzione dominante viene eseguita. Lidea di fondo è che listruzione dominante venga eseguita un numero di volte propor- zionale alla complessità dellalgoritmo, che perciò risulta essere O(d(N)). Negli algoritmi di ordinamento di un array di elementi ricerca di un elemento in un array di elementi listruzione dominante è il confronto fra elementi.

23 ESEMPIO Ricerca esaustiva di un elemento in un array boolean ricerca (int v[], int el){ int i=0; boolean trovato=false; while (i

24 DIPENDENZA DAI DATI DI INGRESSO Spesso accade che il costo di un algoritmo dipenda non solo dalla dimensione dei dati di ingresso, ma anche dai particolari valori dei dati di ingresso ad esempio, un algoritmo che ordina un array può avere un costo diverso secondo se larray è molto disordinato o invece quasi del tutto ordinato analogamente, un algoritmo che ricerca un elemen- to in un array può costare poco, se lelemento viene trovato subito, o molto di più, se lelemento si trova in fondo o è magari del tutto assente.

25 DIPENDENZA DAI DATI DI INGRESSO In queste situazioni occorre distinguere diversi casi: caso migliore caso peggiore caso medio Solitamente la complessità si valuta sul caso peggiore Tuttavia, poiché esso è di norma assai raro, spesso si considera anche il caso medio Caso medio: ogni elemento è equiprobabile

26 ESEMPIO Per la ricerca sequenziale in un array, il costo dipende dalla posizione dellelemento cercato. Caso migliore: lelemento è il primo dellarray un solo confronto Caso peggiore: lelemento è lultimo o non è presente N confronti, costo lineare O(N) Caso medio: lelemento può con egual proba- bilità essere il primo (1 confronto), il secondo (2 confronti),... o lultimo (N confronti) Prob(el(i)) * i = (1/N) * i = (N+1)/2 = O(N/2)

27 ALGORITMI DI ORDINAMENTO Scopo: ordinare una sequenza di elementi in base a una certa relazione dordine –lo scopo finale è ben definito algoritmi equivalenti –diversi algoritmi possono avere efficienza assai diversa Ipotesi: gli elementi siano memorizzati in un array.

28 ALGORITMI DI ORDINAMENTO Principali algoritmi di ordinamento: naïve sort (semplice, intuitivo, poco efficiente) bubble sort (semplice, un po più efficiente) shell sort (generalizza bubble, efficienza media) insert sort (intuitivo, abbastanza efficiente) quick sort (non intuitivo, alquanto efficiente) merge sort (non intuitivo, molto efficiente) Per misurare le prestazioni di un algoritmo, conteremo quante volte viene svolto il con- fronto fra elementi dellarray.

29 NAÏVE SORT Molto intuitivo e semplice, è il primo che viene in mente Specifica(sia n la dimensione dellarray v ) while ( ) { if (p /* invariante: v[n-1] contiene il massimo */ }

30 NAÏVE SORT Codifica void naiveSort(int v[], int n){ int p; while (n>1) { p = trovaPosMax(v,n); if (p

31 NAÏVE SORT Codifica int trovaPosMax(int v[], int n){ int i, posMax=0; for (i=1; i

32 NAÏVE SORT Valutazione di complessità Il numero di confronti necessari vale sempre: (N-1) + (N-2) + (N-3) + … = = N*(N-1)/2 = O(N 2 /2) Nel caso peggiore, questo è anche il numero di scambi necessari (in generale saranno meno) Importante: la complessità non dipende dai particolari dati di ingresso –lalgoritmo fa gli stessi confronti sia per un ar- ray disordinato, sia per un array già ordinato!!

33 BUBBLE SORT Corregge il difetto principale del naïve sort: quello di non accorgersi se larray, a un certo punto, è già ordinato. Opera per passate successive sullarray: –a ogni passata, considera una ad una tutte le possibili coppie di elementi adiacenti, scam- biandoli se risultano nellordine errato –così, dopo ogni passata, lelemento massimo è in fondo alla parte di array considerata Quando non si verificano scambi, larray è ordinato, e lalgoritmo termina.

34 BUBBLE SORT Corregge il difetto principale del naïve sort: quello di non accorgersi se larray, a un certo punto, è già ordinato. Opera per passate successive sullarray: –a ogni passata, considera una ad una tutte le possibili coppie di elementi adiacenti, scam- biandoli se risultano nellordine errato –così, dopo ogni passata, lelemento massimo è in fondo alla parte di array considerata Quando non si verificano scambi, larray è ordinato, e lalgoritmo termina. Può accadere anche alla prima passata, se larray è già ordinato Accorgendosi di array già ordinati, lalgoritmo evita lavoro inutile.

35 BUBBLE SORT Codifica void bubbleSort(int v[], int n){ int i; boolean ordinato = false; while (n>1 && !ordinato){ ordinato = true; for (i=0; iv[i+1]) { scambia(&v[i],&v[i+1]); ordinato = false; } n--; } Continua solo se larray non è an- cora ordinato. A ogni iterazione ipotizza che larray sia ordinato, poi verifica: se deve fare anche solo uno scambio, non era vero.

36 BUBBLE SORT Esempio Iª passata (dim. = 4) al termine, 7 è a posto. IIª passata (dim. = 3) al termine, 6 è a posto. IIIª passata (dim. = 2) al termine, 4 è a posto. array ordinato

37 BUBBLE SORT Valutazione di complessità Caso peggiore: numero di confronti identico al precedente O(N 2 /2) Nel caso migliore, però, basta una sola passata, con N-1 confronti O(N) Nel caso medio, i confronti saranno compresi fra N-1 e N 2 /2, a seconda dei dati di ingresso.

38 SHELL SORT È una variante del bubble sort Opera anchesso per passate successive, considerando coppie di elementi......ma non sono più coppie adiacenti, bensì coppie di elementi a distanza gap Questa distanza gap: –allinizio è la metà della dimensione dellarray –poi, viene ridotta per dimezzamenti successivi.

39 SHELL SORT Ad esempio, su un vettore di 8 elementi: il gap iniziale è 4 si considerano le 4 coppie di elementi di posizione (0,4), (1,5), (2,6), (3,7) poi, il gap si riduce a 2, e si considerano le 6 coppie di elementi di posizione (0,2), (1,3),(2,4),... infine, il gap si riduce a 1, e si considerano le coppie di elementi adiacenti, come nel bubble In caso di scambio, i confronti vengono retro-propagati fino allinizio del vettore, potendo quindi generare nuovi scambi.

40 SHELL SORT Esempio: v = [20, 4, 12, 14, 10, 16, 2] Inizialmente, dim=7, gap = 3 –v = [20, 4, 12, 14, 10, 16, 2] 20>14 scambio (nessuna retropropagazione) Risultato: v = [14, 4, 12, 20, 10, 16, 2] –v = [14, 4, 12, 20, 10, 16, 2] 20>2 scambio Risultato: v = [14, 4, 12, 2, 10, 16, 20] Retropropagazione: v = [14, 4, 12, 2, 10, 16, 20] 14>2 scambio Risultato: v = [2, 4, 12, 14, 10, 16, 20]...

41 SHELL SORT Situazione: v = [2, 4, 12, 14, 10, 16, 20] Ora gap = 3/2 = 1 (coppie adiacenti) –v = [2, 4, 12, 14, 10, 16, 20] nessuno scambio né retropropagazione... –v = [2, 4, 12, 14, 10, 16, 20] 14>10 scambio Risultato: v = [2, 4, 12, 10, 14, 16, 20] Retropropagazione: v = [2, 4, 12, 10, 14, 16, 20] 12>10 scambio Risultato: v = [2, 4, 10, 12, 14, 16, 20]...

42 SHELL SORT Situazione: v = [2, 4, 10, 12, 14, 16, 20] gap vale sempre 1 (coppie adiacenti) –v = [2, 4, 10, 12, 14, 16, 20] nessuno scambio né retropropagazione fine algoritmo Valutazione di complessità difficile da calcolare è stato stimato un caso medio pari a O(N 1.7 )

43 INSERT SORT Approccio originale: per ottenere un array ordinato basta costruirlo ordinato, inseren- do gli elementi al posto giusto fin dallinizio. Idealmente, il metodo costruisce un nuovo array, contenente gli stessi elementi del primo, ma ordinato. In pratica, non è necessario costruire dav- vero un secondo array, in quanto le stesse operazioni possono essere svolte diretta- mente sullarray originale: così, alla fine esso risulterà ordinato.

44 INSERT SORT Scelta di progetto vecchio e nuovo array condividono lo stesso array fisico di N celle (da 0 a N-1) in ogni istante, le prime K celle (numerate da 0 a K-1) costituiscono il nuovo array le successive N-K celle costituiscono la parte residua dellarray originale 01K-1N-1K

45 INSERT SORT Come conseguenza della scelta di progetto fatta, in ogni istante il nuovo elemento da inserire si trova nella cella successiva alla fine del nuovo array, cioè la (K+1)-esima (il cui indice è K) Nuovo array (dim = K) Elemento da inse- rire (indice K) 01K-1K

46 INSERT SORT Specifica for (k=1; k

47 INSERT SORT Esempio Scelta di progetto: se il nuovo array è lungo K=4 (numerate da 0 a 3) lelemento da inserire si trova nella cella successiva (di indice K=4).

48 INSERT SORT Specifica di insMinore() void insMinore(int v[], int pos){ }

49 INSERT SORT Codifica di insMinore() void insMinore(int v[], int pos){ int i = pos-1, x = v[pos]; while (i>=0 && x

50 INSERT SORT Esempio

51 INSERT SORT Valutazione di complessità Nel caso peggiore (array ordinato al contrario), richiede …+(N-1) confronti e sposta- menti O(N 2 /2) Nel caso migliore (array già ordinato), bastano solo N-1 confronti (senza spostamenti) Nel caso medio, a ogni ciclo il nuovo elemento viene inserito nella posizione centrale dellarray 1/2+2/2+…+(N-1)/2 confronti e spostamenti Morale: O(N 2 /4)

52 QUICK SORT Idea base: ordinare un array corto è molto meno costoso che ordinarne uno lungo. Conseguenza: può essere utile partizionare larray in due parti, ordinarle separatamente, e infine fonderle insieme. In pratica: –si suddivide il vettore in due sub-array, delimi- tati da un elemento sentinella (pivot) –il primo array deve contenere solo elementi mi- nori o uguali al pivot, il secondo solo elementi maggiori del pivot.

53 QUICK SORT Algoritmo ricorsivo: i due sub-array ripropongono un problema di ordinamento in un caso più semplice (array più corti) a forza di scomporre un array in sub-array, si giunge a un array di un solo elemento, che è già ordinato (caso banale).

54 QUICK SORT Struttura dellalgoritmo scegliere un elemento come pivot partizionare larray nei due sub-array ordinarli separatamente (ricorsione) Loperazione-base è il partizionamento dellarray nei due sub-array. Per farla: se il primo sub-array ha un elemento > pivot, e il secondo array un elemento < pivot, questi due elementi vengono scambiati Poi si riapplica quicksort ai due sub-array.

55 QUICK SORT Esempio: legenda pivot freccia nera (fine): indica la fine dellarray (e del II° sub-array) freccia nera (iniz): indica linizio dellarray (e del I° sub-array) freccia blu (j): indica la fine del I° sub-array freccia rossa (i): indica linizio del II° sub-array

56 QUICK SORT Esempio (ipotesi: si sceglie 14 come pivot) pivot >2 scambio II° array: solo elementi > pivot I° array: solo elementi pivot 14>10 scambio

57 QUICK SORT Esempio (passo 2: ricorsione sul I° sub-array) pivot elementi > pivot 0 scambi II° array: solo elementi > pivot (dimensione 2) I° array: solo elementi pivot (dimensione 1) elementi pivot 0 scambi elemento singolo (pivot) (già ordinato)

58 QUICK SORT Esempio (passo 3: ricors. sul II° sub-sub-array) 1210 pivot > 10 scambio restano solo due micro- array singoli caso banale

59 QUICK SORT Esempio (passo 4: ricorsione sul II° sub-array) pivot elementi > pivot 0 scambi elementi pivot 0 scambi elemento singolo (pivot) (già ordinato) restano solo due micro- array singoli caso banale

60 QUICK SORT Specifica void quickSort(int v[],int iniz,int fine){ if ( ) }

61 QUICK SORT Codifica void quickSort(int v[],int iniz,int fine){ int i, j, pivot; if (iniz

62 QUICK SORT Codifica void quickSort(int v[],int iniz,int fine){ int i, j, pivot; if (iniz

63 QUICK SORT Codifica do { while (v[i] < pivot) i++; while (v[j] > pivot) j--; if (i < j) scambia(&v[i], &v[j]); if (i <= j) i++, j--; } while (i <= j);

64 QUICK SORT La complessità dipende dalla scelta del pivot: se il pivot è scelto male (uno dei due sub-array ha lunghezza zero), i confronti sono O(N 2 ) se però il pivot è scelto bene (in modo da avere due sub-array di egual dimensione): si hanno log 2 N attivazioni di quicksort al passo k si opera su 2 k array, ciascuno di lunghezza L = N/2 k il numero di confronti ad ogni livello è sempre N (L confronti per ciascuno dei 2 k array) Numero globale di confronti: O(N log 2 N)

65 QUICK SORT Si può dimostrare che O(N log 2 N) è un limite inferiore alla complessità del problema dellordinamento di un array. Dunque, nessun algoritmo, presente o futuro, potrà far meglio di O(N log 2 N) Però, il quicksort raggiunge questo risultato solo se il pivot è scelto bene –per fortuna, la suddivisione in sub-array uguali è la cosa più probabile nel caso medio –lideale sarebbe però che tale risultato fosse raggiunto sempre: a ciò provvede il Merge Sort.

66 MERGE SORT È una variante del quick sort che produce sempre due sub-array di egual ampiezza –così, ottiene sempre il caso ottimo O(N*log 2 N) In pratica: si spezza larray in due parti di ugual dimensione si ordinano separatamente queste due parti (chiamata ricorsiva) si fondono i due sub-array ordinati così ottenuti in modo da ottenere un unico array ordinato. Il punto cruciale è lalgoritmo di fusione (merge) dei due array

67 MERGE SORT Esempio

68 MERGE SORT Specifica void mergeSort(int v[], int iniz, int fine, int vout[]) { if ( ){ }

69 MERGE SORT Codifica void mergeSort(int v[], int iniz, int fine, int vout[]) { int mid; if ( first < last ) { mid = (last + first) / 2; mergeSort(v, first, mid, vout); mergeSort(v, mid+1, last, vout); merge(v, first, mid+1, last, vout); } mergeSort() si limita a suddividere larray: è merge() che svolge il lavoro.

70 MERGE SORT Codifica di merge() void merge(int v[], int i1, int i2, int fine, int vout[]){ int i=i1, j=i2, k=i1; while ( i <= i2-1 && j <= fine ) { if (v[i] < v[j]) vout[k] = v[i++]; else vout[k] = v[j++]; k++; } while (i<=i2-1) { vout[k] = v[i++]; k++; } while (j<=fine) { vout[k] = v[j++]; k++; } for (i=i1; i<=fine; i++) v[i] = vout[i]; }

71 ESPERIMENTI Verificare le valutazioni di complessità che abbiamo dato non è difficile –basta predisporre un programma che conti le istruzioni di confronto, incrementando ogni volta unapposita variabile intera... –... e farlo funzionare con diverse quantità di dati di ingresso Farlo può essere molto significativo.

72 ESPERIMENTI Risultati: per problemi semplici, anche gli algoritmi poco sofi- sticati funzionano abbastanza bene, a volte meglio degli altri quando invece il problema si fa complesso, la differenza diventa ben evidente.

73 LINK Alcuni link a cui si trovano algoritmi di ordi- namento e relative animazioni: sis.bris.ac.uk/~je7796/tech/sort.htm

74 ORDINARE ARRAY DI TIPI COMPLESSI Finora abbiamo considerato sempre array di interi, ai quali sono applicabili gli operatori –uguaglianza == –disuguaglianza!= –minore< –maggiore> –minore o uguale<= –maggiore o uguale>= Nella realtà però accade spesso di trattare strutture dati di tipi non primitivi.

75 ORDINARE ARRAY DI TIPI COMPLESSI Ad esempio, un array di persona : typedef struct { char nome[20], cognome[20]; int annoDiNascita; } persona; A una persona non si possono applicare gli operatori predefiniti! Come generalizzare gli algoritmi di ordina- mento a casi come questo?

76 ORDINARE ARRAY DI TIPI COMPLESSI Per generalizzare gli algoritmi di ordinamen- to a casi come questo, occorre: –eliminare dagli algoritmi ogni occorrenza degli operatori relazionali predefiniti ( ==, >, etc.) –sostituirli con chiamate a funzioni da noi definite che svolgano il confronto nel modo specifico del tipo da trattare. Così, ad esempio, potremmo avere: –uguaglianza boolean isEqual(...) –minore boolean isLess(...) –...

77 GENERALIZZARE GLI ALGORITMI Una soluzione semplice e pratica consiste nell impostare tutti gli algoritmi in modo che operino su array di element. Il tipo element sarà poi da noi definito caso per caso: –nel caso banale, element = int element = float... –in generale, element = persona element =...

78 GENERALIZZARE GLI ALGORITMI Ad esempio, il bubble sort diventa: void bubbleSort(int v[], int n){ int i; boolean ordinato = false; while (n>1 && !ordinato){ ordinato = true; for (i=0; i

79 IL TIPO element Per definire element si usa la solita struttura: element.h –fornirà la definizione del tipo element (adeguatamente protetta dalle inclusioni multiple) –conterrà le dichiarazioni degli operatori element.c –includerà element.h –conterrà le definizioni degli operatori il cliente (algoritmo di ordinamento) –includerà element.h –userà il tipo element per operare.

80 IL TIPO element Per definire element si usa la solita struttura: element.h –fornirà la definizione del tipo element (adeguatamente protetta dalle inclusioni multiple) –conterrà le dichiarazioni degli operatori element.c –includerà element.h –conterrà le definizioni degli operatori il cliente (algoritmo di ordinamento) –includerà element.h –userà il tipo element per operare. Rimane quasi identico quando element cambia Si modifica solo la typedef. Va cambiato caso per caso in modo da adattarsi allelement considerato.

81 IL TIPO element element.h #ifndef element_h #define element_h typedef.... element; #endif #include boolean.h boolean isEqual(element, element); boolean isLess(element, element); void printElement(element); void readElement(element*); Protezione dalle inclusioni multiple element usa boolean

82 IL TIPO element element.c #include element.h boolean isEqual(element e1, element e2){... } boolean isLess(element e1, element e2){... }...

83 ESEMPIO: element = persona Ipotesi: persona è definita in persona.h element.h #include persona.h #ifndef element_h #define element_h typedef persona element; #endif /* il resto non si tocca ! */... Include la definizione del tipo persona Stabilisce che in questo caso element equivale a persona

84 ESEMPIO: element = persona element.c –luguaglianza fra persone: #include element.h boolean isEqual(element e1, element e2){ return strcmp(e1.nome,e2.nome)==0 && strcmp(e1.cognome, e2.cognome)==0 && e1.annoDiNascita==e2.annoDiNascita; }... Potrei anche stabilire di considerare uguali due persone se hanno anche solo il cognome uguale, o magari cognome e nome ma non lanno, etc. Il criterio di uguaglianza lo stabiliamo noi.

85 ESEMPIO: element = persona element.c –lordinamento fra persone (ipotesi: vogliamo ordinare le persone per cognome e in subordine per nome)... boolean isLess(element e1, element e2){ int cognomeMinore = strcmp(e1.cognome, e2.cognome); if (cognomeMinore<0) return 1; if (cognomeMinore>0) return 0; return strcmp(e1.nome, e2.nome)<0; }

86 ESEMPIO: element = persona element.c –lordinamento fra persone (ipotesi: vogliamo ordinare le persone per cognome e in subordine per nome)... boolean isLess(element e1, element e2){ int cognomeMinore = strcmp(e1.cognome, e2.cognome); if (cognomeMinore<0) return 1; if (cognomeMinore>0) return 0; return strcmp(e1.nome, e2.nome)<0; } if (cognomeMinore!=0) return (cognomeMinore<0);

87 ALGORITMI DI RICERCA Cercare un elemento in un vettore: in generale, richiede di controllare tutti gli elementi (fino a che non si trova lelemento, o il vettore è finito) se però il vettore è ordinato può essere fatto in modo molto più efficiente, sfruttando lor- dinamento per andare a colpo sicuro a cercare lelemento richiesto.

88 RICERCA BINARIA Lalgoritmo emula ciò che si fa quando si cerca, a mano, una parola in un dizionario: si apre il dizionario circa alla posizione dove dovrebbe trovarsi la parola da cercare Si confronta lelemento da cercare con quel- lo di posizione mediana nellarray. Così: o lelemento viene trovato subito oppure si sa dove continuare la ricerca a sinistra (fra gli elementi minori), se lelemento mediano è maggiore di quello richiesto a destra (fra gli elementi maggiori) in caso contrario.

89 RICERCA BINARIA 1) si confronta lelemento da cercare con quello di posizione mediana 2) se è lelemento cercato, la ricerca si conclude con successo -- altrimenti la ricerca prosegue nella metà di sinistra dellarray se lelemento mediano è maggiore di quello richiesto nella metà di destra dellarray se lelemento mediano è minore di quello richiesto.

90 RICERCA BINARIA A ogni ciclo si dimezza lo spazio di ricerca Caso migliore: lelemento cercato viene trovato al primo colpo 1 confronto Caso peggiore: lelemento non è presente (occorre controllare tutto larray) –a ogni passo si effettua un confronto –ad ogni passo la dimensione dellarray si dimezza al passo K, larray è lungo N / 2 K-1 –quando larray è lungo 1, ci si ferma ciò accade per K tale che 2 K-1 N, ossia K = 1+ log 2 N Numero di confronti: O(log 2 N)

91 RICERCA BINARIA Esempio

92 RICERCA BINARIA Occorre una funzione che: distingua il caso elemento trovato dal caso elemento non trovato nel caso elemento trovato, indichi anche dove. Specifica int ricBin(int v[], int dim, int el); restituisce la posizione dellelemento el nellarray v (che è lungo dim ), cioè un intero 0 se lelemento non è presente nellarray, per conven- zione restituisce -1

93 RICERCA BINARIA Specifica int ricBin(int v[], int dim, int el){ if ( ) if ( ) return 0; /* posizione */ else return –1; /* non trovato */ while( ) if (v[m]==el) return m; /* trovato */ if (v[m] else return –1; }

94 RICERCA BINARIA Specifica int ricBin(int v[], int dim, int el){ if ( ) if ( ) return 0; /* posizione */ else return –1; /* non trovato */ while( ) if (v[m]==el) return m; if (v[m] else return –1; } Useremo due indici first e last per indicare la parte di array su cui concentriamo la nostra atten- zione. Allinizio, first = 0 e last = dim-1 Seconda metà: first = m+1, last intoccato Prima metà: first intoccato, last = m-1

95 RICERCA BINARIA Codifica int ricBin(int v[], int dim, int el){ int m, first=0, last=dim-1; if (last==0) if (v[last]==el) return 0; else return –1; while(last >= first) { m = (last + first)/2; if (v[m]==el) return m; if (v[m]

96 RICERCA BINARIA Una versione tail-ricorsiva: int ricBinTail(int v[], int el, int first, int las t); Chiamata: (ricerca di x in un array w, lungo 8) pos = ricBinTail(w, x, 0, 7); Specifica: –se larray è lungo 1, restituisci -1 se lelemento è diverso da v[0], altrimenti restituisci 0 (la posizione) –se larray è più lungo, determina quale metà del- larray considerare, e restituisci il risultato della chiamata ricorsiva a ricBinTail(w,x,...,...)

97 RICERCA BINARIA Come generalizzare al caso di un array di elementi generici (non solo int )? Provare ad adattare lalgoritmo! –usare il tipo element –eliminare dalle funzioni ricBin (e ricBintail ) ogni dipendenza dal tipo int (operatori relazio- nali di confronto, etc.), inserendo chiamate alle funzioni previste ( isEqual, isLess, etc.)


Scaricare ppt "COMPLESSITÀ DEGLI ALGORITMI Tra i problemi che ammettono soluzione ne esistono di più facili e di più difficili. Teoria della complessità (anni 70): complessità"

Presentazioni simili


Annunci Google