Algoritmi su Array Moreno Marzolla Dipartimento di Informatica—Scienza e Ingegneria (DISI) Università di Bologna http://www.moreno.marzolla.name/
Copyright © 2008, Stefano Mizzaro http://users.dimi.uniud.it/~stefano.mizzaro/dida/Prog0708/ Copyright © 2016, 2017, Moreno Marzolla http://www.moreno.marzolla.name/teaching/FINFA/ This work is licensed under the Creative Commons Attribution-Non Commercial 2.0 (CC BY-NC 2.0) License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/2.0/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. Array
Ringraziamenti prof. Stefano Mizzaro, Università di Udine http://users.dimi.uniud.it/~stefano.mizzaro/ Array
Esempi classici con array Inversione Ricerca lineare Ricerca binaria Ordinamento Array
Inversione Array
Inversione di un array Scambiare di posto gli elementi di un array a[] di n elementi Il primo e l’ultimo Il secondo e il penultimo … (fermarsi a metà!!) Prima 7 12 3 -1 8 2 1 3 2 15 15 2 3 1 2 8 -1 3 12 7 Dopo Array
Inversione di un array Considero il primo e l'ultimo elemento e li scambio 7 12 3 -1 8 2 1 3 2 15 Array
Inversione di un array Considero il secondo e il penultimo elemento e li scambio 15 12 3 -1 8 2 1 3 2 7 Array
Inversione di un array ...e vado avanti così Devo però fermarmi a metà dell'array! Perché? 15 12 3 -1 8 2 1 3 2 7 Array
L’algoritmo Prima versione Considero il primo e l’ultimo e li scambio Considero il secondo e il penultimo e li scambio … Fino a metà array "Pseudo"-pseudocodice i = 0; j = n-1; while ("non sono arrivato a metà") { "scambia a[i] con a[j]" i = i + 1; j = j – 1; } Array
Raffinamento Come decido se non sono ancora arrivato a metà? i j 7 12 3 -1 8 2 1 3 2 15 i j 7 12 3 -1 8 2 1 3 2 15 i = 0; j = n-1; while (i < j) { "scambia a[i] con a[j]" i = i + 1; j = j – 1; } Array
Come (non) scambiare il valore di due variabili int a = 3, b = 5; /* modo SBAGLIATO di scambiare tra loro i valori di a e b */ a = b; b = a; Quale è il risultato del frammento di codice sopra? Array
Come scambiare il valore di due variabili 3 bicchieri, a, b e tmp ("temporaneo") In a c’è acqua, in b c’è vino, tmp è vuoto Voglio “scambiare” a e b (mettere il vino in a e l’acqua in b). Come faccio? a b tmp Array
Come scambiare il valore di due variabili 3 bicchieri, a, b e tmp ("temporaneo") In a c’è acqua, in b c’è vino, tmp è vuoto Voglio “scambiare” a e b (mettere il vino in a e l’acqua in b). Come faccio? Verso a in tmp a b tmp Array
Come scambiare il valore di due variabili 3 bicchieri, a, b e tmp ("temporaneo") In a c’è acqua, in b c’è vino, tmp è vuoto Voglio “scambiare” a e b (mettere il vino in a e l’acqua in b). Come faccio? Verso a in tmp Verso b in a a b tmp Array
Come scambiare il valore di due variabili 3 bicchieri, a, b e tmp ("temporaneo") In a c’è acqua, in b c’è vino, tmp è vuoto Voglio “scambiare” a e b (mettere il vino in a e l’acqua in b). Come faccio? Verso a in tmp Verso b in a Verso tmp in b a b tmp Array
Come scambiare il valore di due variabili /* modo CORRETTO di scambiare tra loro i valori di a e b */ int a = 3, b = 5, tmp; tmp = a; /* “verso” a in tmp */ a = b; /* “verso” b in a */ b = tmp; /* “verso” tmp in a */ Array
Come scambiare due elementi di un array Voglio “scambiare” a[i] e a[j]. Come faccio? tmp = a[i]; a[i] = a[j]; a[j] = tmp; 2. a[i] a[j] tmp 1. 3. Array
Inversione di un array /* inversione.c : inverte il contenuto di un array */ #include <stdio.h> void inverti(int a[], int n) { int i = 0, j = n-1, tmp; while ( i < j ) { tmp = a[i]; a[i] = a[j]; a[j] = tmp; i++; j--; } #define N 10 int main( void ) int a[N], i; printf("Digita %d valori\n", N); for (i = 0; i < N; i++) { /* Lettura dell'array */ scanf("%d", &a[i]); inverti(a, N); for (i = 0; i < N; i++) { /* Stampa l'array dopo l'inversione */ printf("%d\n", a[i]); return 0; (così si legge in input un array) Array
Ricerca lineare Array
Ricerca lineare Dati Un array a[] di int La lunghezza n dell'array Un intero k int ricerca(int a[], int n, int k) Restituisce la posizione di una occorrenza di k in a[] Se k non compare in a, restituisce -1 Procedimento Scorro l’array partendo dall’inizio Fermandomi se trovo un elemento il cui valore è uguale a k, oppure se arrivo in fondo all'array k 3 7 12 3 -1 8 2 1 3 2 15 a[] Array 1 2 3 4 5 6 7 8 9
Ricerca lineare Primo tentativo (sbagliato) int ricerca(int a[], int n, int k) { int i; for (i = 0; i < n; i++) { if (a[i] == k) { "trovato" } else { "non trovato" } SBAGLIATO Con il while? k 3 a[] 7 12 3 -1 8 2 1 3 2 15 Array 1 2 3 4 5 6 7 8 9
Ricerca lineare Secondo tentativo (meglio) int ricerca(int a[], int n, int k) { int i; for (i = 0; i < n; i++) { if (a[i] == k) { "trovato" } "non trovato" OK k 3 a[] 7 12 3 -1 8 2 1 3 2 15 Array 1 2 3 4 5 6 7 8 9
Ricerca lineare /* ricerca-lineare.c */ #include <stdio.h> int ricerca(int a[], int n, int k) { int i; for (i = 0; i < n; i++) { if (a[i] == k) { return i; } return -1; int main( void ) int a[] = {7, 12, 3, -1, 8, 2, 1, 3, 2, 15}; printf("ricerca 7 = %d\n", ricerca(a, 10, 7)); printf("ricerca 15 = %d\n", ricerca(a, 10, 15)); printf("ricerca 3 = %d\n", ricerca(a, 10, 3)); printf("ricerca 17 = %d\n", ricerca(a, 10, 17)); return 0; Ricordare che l'istruzione return termina immediatamente l'esecuzione della funzione, restituendo il valore indicato Array
Una applicazione della ricerca lineare Il sindaco di Paperopoli ha deciso di tassare i residenti in base al reddito. Detto R il reddito lordo annuo, l'importo dovuto in tasse è (R ´ x), dove x dipende da R secondo la seguente tabella Se 0 ≤ R < 10000 → x = 0.05 Se 10000 ≤ R < 22000 → x = 0.11 Se 22000 ≤ R → x = 0.15 Scrivere un programma in C che chiede all'utente di inserire il reddito R (tipo double; se l'utente inserisce un valore negativo, richiederlo), e stampa l'importo dovuto in tasse Array
Primo tentativo: funziona, ma... /* tasse-1.c */ #include <stdio.h> int main( void ) { double R, x; do { printf("Inserire reddito R\n"); scanf("%lf", &R); } while (R < 0); if ( R < 10000.0) { x = 0.05; } else { if ( R < 22000.0 ) { x = 0.11; x = 0.15; } printf("Le tasse dovute ammontano a %f\n", R*x); return 0; Array
Un bel giorno... Vista la situazione disastrata delle finanze di Paperopoli, il sindaco decide di introdurre nuovi scaglioni: Se 0 ≤ R < 10000 → x = 0.05 Se 10000 ≤ R < 22000 → x = 0.11 Se 22000 ≤ R < 35000 → x = 0.15 Se 35000 ≤ R < 45000 → x = 0.17 Se 45000 ≤ R < 57000 → x = 0.20 Se 57000 ≤ R → x = 0.25 Modificare il programma precedente per funzionare correttamente nella nuova situazione Array
/* tasse-2.c */ #include <stdio.h> int main( void ) { double R, x; do { printf("Inserire reddito R\n"); scanf("%lf", &R); } while (R < 0); if ( R < 10000.0) { x = 0.05; } else { if ( R < 22000.0 ) { x = 0.11; if ( R < 35000.0 ) { x = 0.15; if ( R < 45000.0 ) { x = 0.17; if ( R < 57000.0 ) { x = 0.20; x = 0.25; } printf("Le tasse dovute ammontano a %f\n", R*x); return 0; Array
/* tasse-2.c */ #include <stdio.h> int main( void ) { double R, x; do { printf("Inserire reddito R\n"); scanf("%lf", &R); } while (R < 0); if ( R < 10000.0) { x = 0.05; } else { if ( R < 22000.0 ) { x = 0.11; if ( R < 35000.0 ) { x = 0.15; if ( R < 45000.0 ) { x = 0.17; if ( R < 57000.0 ) { x = 0.20; x = 0.25; } printf("Le tasse dovute ammontano a %f\n", R*x); return 0; Array
Idea Memorizzo gli scaglioni e le relative percentuali in una "tabella" Dato il reddito R, cerco nella "tabella" lo scaglione corrispondente e da questo ricava la percentuale Se gli estremi degli scaglioni e/o le percentuali cambiano, devo cambiare solo la tabella e non il programma che fa la ricerca! Array
Nuova soluzione: meglio /* tasse-3.c */ #include <stdio.h> int main( void ) { const int nscaglioni = 6; double scaglioni[] = { 0.0, 10000.0, 22000.0, 35000.0, 45000.0, 57000.0}; double imposta[] = {0.05, 0.11, 0.15, 0.17, 0.20, 0.25}; double R, x; int i; do { printf("Inserire reddito R\n"); scanf("%lf", &R); } while (R < 0); for (i=0; (i < nscaglioni) && (R >= scaglioni[i]); i++) { x = imposta[i]; } printf("Le tasse dovute ammontano a %f (imposta=%f%%)\n", R*x, x*100); return 0; scaglioni[i] è il limite inferiore dello scaglione i-esimo (inizio a contare da i = 0). imposta[i] è la percentuale di tasse per i redditi appartenenti allo scaglione i-esimo Array
Esercizio 6 cassetti di resistenze etichettati con i seguenti valori (in Ohm): 10, 15, 22, 33, 47, 68 I valori hanno una tolleranza di +/- 10% Significa. ad es., che il cassetto etichettato 15 può contenere resistenze con 15 ´ 0.9 ≤ R ≤ 15 ´ 1.1 Nota: gli intervalli NON si sovrappongono Data una resistenza di valore misurato R, determinare in quale cassetto deve essere riposta E' possibile che R sia tale da non poter essere inserito in alcun cassetto; in tal caso il programma lo deve segnalare ad es. con un opportuno messaggio d'errore Assumere che tutti i valori siano di tipo float Array
10 15 22 33 47 68 R = 21 Array
Possibile soluzione /* resistenza.c */ #include <stdio.h> int main( void ) { const int n = 6; /* numero di cassetti */ float val[] = {10, 15, 22, 33, 47, 68}; float R; int i; printf("Inserire R\n"); scanf("%f", &R); for (i=0; i<n; i++) { if ( R >= val[i]*0.9 && R <= val[i]*1.1 ) break; } if ( i < n ) { printf("Resistenza R=%f nel cassetto etichettato %f\n", R, val[i]); } else { printf("Resistenza R=%f non inseribile in alcun cassetto\n", R); return 0; Array
Ricerca Binaria Array
Ricerca binaria Dati: Un array di valori reali distinti v[0], v[1], … v[n-1] ordinati in senso crescente (v[0] < v[1] < … < v[n-1]) Un valore reale k (arbitrario) Determinare la posizione di k nell'array (se presente) Cioè determinare l'indice i, se esiste, tale che v[i] == k Se k non compare nell'array, restituire -1 Es: v[0] v[1] v[2] v[3] v[4] v[5] v[6] v[7] Cerchiamo k = 13 -3 -1 2 5 6 13 15 21 Array La funzione restituisce 5 Vedere i lucidi sugli algoritmi
Funzionamento dell'algoritmo Usiamo due valori interi i (inizio) e f (fine) per rappresentare la posizione (indice) del primo e dell'ultimo elemento della porzione di array in cui potrebbe trovarsi il valore cercato Indichiamo con m (mezzo) l'indice dell'elemento che occupa la posizione centrale nel sottovettore v[i] … v[f] i=0 m=3 f=6 v[0] v[1] v[2] v[3] v[4] v[5] v[6] 2 3 7 12 18 21 27 Cerchiamo k = 3 Algoritmi
Pseudocodice i = 0; f = n – 1; while (f ≥ i) { m = (i + f) / 2; if (v[m] == k) { "trovato in posizione m" } else { if (v[m] > k) { f = m – 1; i = m + 1; } "non trovato" Algoritmi
/* ricerca-binaria.c */ #include <stdio.h> int ricbinaria(int v[], int n, int k) { int i = 0, f = n-1, m; while (f >= i) { m = (i + f) / 2; if (v[m] == k) { return m; /* trovato in posizione m */ } else { if (v[m] > k) { f = m - 1; i = m + 1; } return -1; /* non trovato */ int main( void ) int a[] = {-1, 1, 2, 3, 7, 15, 16, 18, 20, 21}; printf("ricerca 7 = %d\n", ricbinaria(a, 10, 7)); printf("ricerca -1 = %d\n", ricbinaria(a, 10, -1)); printf("ricerca 17 = %d\n", ricbinaria(a, 10, 17)); printf("ricerca -2 = %d\n", ricbinaria(a, 10, -2)); return 0; Algoritmi
Cosa succede se l'array contiene valori duplicati? /* ricerca-binaria.c */ #include <stdio.h> int ricbinaria(int v[], int n, int k) { int i = 0, f = n-1, m; while (f >= i) { m = (i + f) / 2; if (v[m] == k) { return m; /* trovato in posizione m */ } else { if (v[m] > k) { f = m - 1; i = m + 1; } return -1; /* non trovato */ int main( void ) int a[] = {-1, 1, 2, 3, 7, 15, 16, 18, 20, 21}; printf("ricerca 7 = %d\n", ricbinaria(a, 10, 7)); printf("ricerca -1 = %d\n", ricbinaria(a, 10, -1)); printf("ricerca 17 = %d\n", ricbinaria(a, 10, 17)); printf("ricerca -2 = %d\n", ricbinaria(a, 10, -2)); return 0; Cosa succede se la funzione ricbinaria() viene invocata su un array vuoto (n = 0)? Cosa succede se l'array contiene valori duplicati? Algoritmi
Ordinamento Algoritmi
Ordinamento Ordinare un array Problema classico Capita spesso in moltissime applicazioni Vari algoritmi Ne vediamo uno (Selection Sort) Semplice da capire Non molto efficiente Array
Algoritmi e Strutture Dati Selection Sort 12 7 3 2 14 22 1 3 1 7 3 2 14 22 12 3 Cerco il minimo in a[0]...a[n-1] e lo scambio con a[0] Cerco il minimo in a[1]...a[n-1] e lo scambio con a[1] ... Cerco il minimo in a[i]...a[n-1] e lo scambio con a[i] 1 2 3 7 14 22 12 3 1 2 3 7 14 22 12 3 1 2 3 3 14 22 12 7 1 2 3 3 7 22 12 14 1 2 3 3 7 12 22 14 Algoritmi e Strutture Dati 1 2 3 3 7 12 14 22
Algoritmi e Strutture Dati Pseudocodice Prima versione Raffinamento for i ← 0 to n – 2 do "Determina il valore minimo v[j] del sottovettore v[i] .. .v[n – 1]" "Scambia v[i] con v[j]" endfor for i ← 0 to n–2 do for j ← i+1 to n–1 do if (a[i] > a[j]) then "scambia a[i] con a[j]" endif endfor Algoritmi e Strutture Dati
Algoritmi e Strutture Dati 12 7 3 2 14 22 1 3 i j 7 12 3 2 14 22 1 3 3 12 7 2 14 22 1 3 2 12 7 3 14 22 1 3 2 12 7 3 14 22 1 3 2 12 7 3 14 22 1 3 1 12 7 3 14 22 2 3 Algoritmi e Strutture Dati
Algoritmi e Strutture Dati Costo computazionale Quante volte viene eseguito il blocco di codice evidenziato? for i ← 0 to n–2 do for j ← i+1 to n–1 do if (a[i] > a[j]) then "scambia a[i] con a[j]" endif endfor Il blocco viene eseguito (n-1) + (n-2) + … + 2 + 1 volte Algoritmi e Strutture Dati
Algoritmi e Strutture Dati = una iterazione del ciclo "for" interno (quello con indice j) n - 1 i → 1 2 3 4 n-2 Algoritmi e Strutture Dati
Algoritmi e Strutture Dati = una iterazione del ciclo "for" interno (quello con indice j) n - 1 n n - 1 i → 1 2 3 4 n-2 Algoritmi e Strutture Dati
Il codice /* selection-sort.c */ #include <stdio.h> void selectionsort(int a[], int n) { int i, j, tmp; for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (a[i] > a[j]) { tmp = a[i]; /* scambio a[i] con a[j] */ a[i] = a[j]; a[j] = tmp; } #define N 10 int main( void ) int a[N], i; for (i = 0; i < N; i++) { scanf(“%d”, &a[i]); } selectionsort(a, N); for (i = 0; i < N; i++) { printf(“%d\n”, a[i]); } return 0; Array