ALGORITMI DI ORDINAMENTO Cinzia Reverberi COS’È UN ALGORITMO? Un algoritmo è un insieme ben ordinato di operazioni non ambigue ed effettivamente calcolabili che, eseguito, produce un risultato e termina in una quantità finita di tempo. In pratica, i punti principali di un algoritmo sono: -un insieme ben ordinato di operazioni -operazioni non ambigue e calcolabili -produce un risultato -termina in una quantità finita di tempo ALGORITMO DI ORDINAMENTO E’ un algoritmo che viene utilizzato per elencare gli elementi di un insieme secondo una sequenza stabilita da una relazione d’ordine. Vi sono numerosi tipi di algoritmi di ordinamento: -quelli più semplici, iterativi: * insertion sort * selection sort * bubble sort -applicabili solo in casi particolari: * counting sort * radix sort * bin o bucket sort -più complessi, ricorsivi: * merge sort * quicksort * heapsort INSERTION SORT È un algoritmo relativamente semplice che si occupa di ordinare un array. Esso si occupa di ciò senza l’utilizzo di un array di appoggio (algoritmo in place). L’algoritmo tende a spostare gli elementi maggiori verso destra: utilizza due indici. Il primo punta al secondo elemento dell’array, mentre il secondo punta al primo. Se il primo elemento è maggiore del secondo i due elementi vengono scambiati e successivamente il primo indice avanza di una posizione mentre il secondo parte da quello precedentemente puntato dal primo. #include <stdlib.h> #include <stdio.h> #define MAX 100 /* * Legge in input il numero n ed n numeri interi * che memorizza nell'array. Restituisce il numero * di elementi letti (n). */ int leggi_array(int x[ ]) { int i, n; printf("Numero di elementi: "); scanf("%d", &n); printf("Inserisci %d elementi: ", n) ; for (i=0; i<n; i++) scanf("%d", &x[i]); return(n); } /* * Stampa in output l'array. * void stampa_array(int x[ ], int n) { int i; for (i=0; i<n; i++) printf("%d ", x[i]); printf("\n"); return; } /* * Funzione che implementa l'algoritmo Insertion sort. * Riceve come argomento l'array ed il numero di * elementi contenuti nell'array. Non restituisce alcun * valore, ma modifica il contenuto dell'array, ordinandolo. */ void insertion_sort (int x[ ], int n) { int i, j, app; for (i=1; i<n; i++) { app = x[i]; j = i-1; while (j>=0 && x[j]>app) { x[j+1] = x[j]; j--; } x[j+1] = app; } return; } /* * Funzione principale */ int main(void) { int v[MAX], n; n = leggi_array(v); stampa_array(v, n); insertion_sort(v, n); stampa_array(v, n); return(1); } SELECTION SORT Come l’algoritmo precedente, opera in place. I passi che compie l’algoritmo per ordinare l’array sono i seguenti: - si inizializza un puntatore i che va da 1 a n (dove n è la lunghezza dell'array). -Si cerca il più piccolo elemento dell'array -Scambia l'elemento più piccolo con l'elemento alla posizione i -Incrementa l'indice i e si torna al passo uno fino alla fine dell'array. /* SELECTION SORT Ricerca il minimo nella parte di array non ancora ordinata e lo ordina. Poi prosegue con altre ricerche ripetute nella parte di array non ancora ordinata. */ void selection_sort(int a[], int n) { //funzione tipo void, effettua modifiche permanenti e non restituisce nulla . int min = a[0], i = 0, j, p = 0; //dichiariamo il minimo dell'array uguale alla sua prima componente. while (i < n) { //scansioniamo tutto l'array for (j = i; j < n; j++){ //scansiona tutto l'array partendo da una posizione in più poichè uscendo da qua 1 elemento sarà già ordinato. if (a[j] < min) { //se l'elemento attuale è minore del minimo allora l'elemento attuale sarà il nuovo minimo min = a[j]; p = j; //memorizziamo la posizione del minimo nell'array } } scambia (&a[i],&a[p]); //uscendo dal ciclo for scambiamo di posizione l'elemento iniziale della scansione con il minimo trovato. i++; //incrementiamo i per passare a scansionare il restante array non ancora ordinato. min = a[i]; //settiamo il minimo uguale all'elemento dopo. } } BUBBLE SORT È un semplice algoritmo di ordinamento. Può essere utilizzato per ordinare tutti i tipi di elementi su cui sia definita una relazione d’ordine. Il suo nome deriva dal fatto che durante il suo procedimento ci sono dati che si muovono nell’array velocemente e altri che lo fanno più lentamente. Ciò ricorda il movimento delle bollicine in un bicchiere di champagne. /* BUBBLESORT - Ordinamento di un Array. */ void bubblesort (int a[], int n) { int i, j; // N.B. n-1 è l'ultima componente dell'array. for(i = 0; i < n - 1; i++) //scansiona tutto l'array tranne l'ultima componente: n - 1 escluso. Quindi fino al penultimo elemento. for (j = n - 1; j > i; j--) //j settato all'ultima componente e decresce ad ogni iterazione. Esce dal ciclo solo se j <= i if (a[j] < a[j-1]) //se la componente corrente è più piccola della precedente, li scambia. scambia(&a[j],&a[j-1]); } //la funzione scambia è così composta: void scambia(int *a, int *b) { //richiede due indirizzi di memoria in entrata che verranno memorizzati in 2 puntatori. int temp; //la variabile d'appoggio che memorizza temporaneamente il valore di a. temp = *a; *a = *b; *b = temp; } //NB. l'asterisco * dei puntatori fa riferimento sempre al valore a cui punta il puntatore. COUNTING SORT Algoritmo di ordinamento per valori numerici interi L'algoritmo è semplice ed intuitivo: si calcolano i valori max(A) e min (A) e si prepara un array C di dimensione pari all'intervallo dei valori con C[i] che rappresenta la frequenza dell'elemento i+min(A) nell'array di partenza A. Si visita l'array A aumentando l'elemento di C corrispondente. Dopo si visita l'array C in ordine e si scrivono su A, C[i] copie del valore i+min(A). RADIX SORT è un algoritmo di ordinamento per valori numerici interi, dove l è la lunghezza dell'array e m è la media del numero di cifre dei numeri. Esegue gli ordinamenti per posizione della cifra ma partendo dalla cifra meno significativa. Questo affinché l'algoritmo non si trovi a dovere operare ricorsivamente su sottoproblemi di dimensione non valutabili a priori. BUCKET SORT è un algoritmo di ordinamento per valori numerici interi si prepara un array C di dimensione pari a m (cioè al valore massimo che può essere nell'array) con C[i] che rappresenta la frequenza dell'elemento i nell'array di partenza A. Si visita l'array A aumentando l'elemento di C corrispondente. Dopo si visita l'array C in ordine e si scrivono su A, C[i] copie del valore i. #include <stdio.h> void bucketSort(int array[], int n) { int i, j; int count[n]; for(i=0; i < n; i++) { count[i] = 0; } for(i=0; i < n; i++) { (count[array[i]])++; } for(i=0,j=0; i < n; i++) { for(; count[i]>0; (count[i])--) { array[j++] = i; } } } int main() { int array[] = {1,3,4,6,4,2,9,1,2,9}; int n = 10; int i; for (i = 0;i < n;i++) { printf("%d ", array[i]); } printf("\n"); bucketSort(array, n); for (i = 0;i < n;i++) { printf("%d ", array[i] ); } printf("\n"); return 0; } MERGE SORT consiste nella suddivisione del problema in sottoproblemi via via più piccoli. Il merge sort opera quindi dividendo l'insieme da ordinare in due metà e procedendo all'ordinamento delle medesime ricorsivamente. Quando si sono divise tutte le metà si procede alla loro fusione (merge appunto) costruendo un insieme ordinato /* * Legge in input il numero n ed n numeri interi * che memorizza nell'array. Restituisce il numero * di elementi letti (n). */ int leggi_array(int V[]) { int n, i; printf("Numero di elementi: "); scanf("%d", &n); for (i=0; i<n; i++) scanf("%d", &V[i]); return(n); } /* * Stampa in output l'array. */ void stampa_array(int V[], int n) { int i; for (i=0; i<n; i++) { printf("%d ", V[i]); } return; } /* * Funzione Merge per la fusione di due * componenti ordinate dell'array. */ void Merge(int A[], int p, int q, int r) { int i, j, k, B[MAX]; i = p; j = q+1; k = 0; while (i<=q && j<=r) { if (A[i]<A[j]) { B[k] = A[i]; i++; } else { B[k] = A[j]; j++; } k++; } while (i<=q) { B[k] = A[i]; i++; k++; } while (j<=r) { B[k] = A[j]; j++; k++; } f or (k=p; k<=r; k++) A[k] = B[k-p]; return; } /* * Funzione ricorsiva MergeSort. */ void MergeSort(int A[], int p, int r) { int q; f (p<r) { q = (p+r)/2; MergeSort(A, p, q); MergeSort(A, q+1, r); Merge(A, p, q, r); } return; } /* * Funzione principale */ int main(void) { int n, V[MAX]; n = leggi_array(V); MergeSort(V, 0, n-1); stampa_array(V, n); return(1); } QUICKSORT è un ottimo algoritmo di ordinamento ricorsivo in place a base del suo funzionamento è l'utilizzo ricorsivo della procedura partition: preso un elemento da una struttura dati (es. array) si pongono gli elementi minori a sinistra rispetto a questo e gli elementi maggiori a destra. void scambia (tipobase v[], long i, long j) { tipobase tmp=v[i]; v[i]=v[j]; v[j]=tmp; } void QSort (tipobase v[], long inf, long sup) { tipobase pivot=v[(inf+sup)/2]; long i=inf,j=sup; while (i<=j) { while (v[i]<pivot) i++; while (v[j]>pivot) j--; if (i<j) scambia(v,i,j); if (i<=j) { i++; j--; } } if (inf<j) QSort(v,inf,j); if (i<sup) QSort(v,i,sup); } HEAPSORT basa la sua potenza sull’utilizzo di una struttura dati chiamata Heap, che gestisce intelligentemente le informazioni durante l’esecuzione dell’algoritmo di ordinamento.