ALGORITMI DI SORT Hanno come obiettivo l'ordinamento crescente o decrescente di un insieme di dati; uno dei casi più interessanti riguarda l'ordinamento dei valori memorizzati in un vettore (numeri interi, reali, stringhe). È importante sottolineare fin da subito che non esiste un algoritmo migliore in assoluto: alcuni sono da preferire quando si sa che il vettore è già quasi ordinato (quindi con pochi elementi fuori posto), altri quando invece il vettore è molto disordinato; alcuni è conveniente usarli con vettori sufficientemente grandi perché si basano su tecniche più sofisticate che aggiungono dei tempi che si ammortizzano solo se il numero di elementi è elevato. Alcuni sono particolarmente veloci ma altrettanto voraci di RAM. Qualunque sia l'algoritmo esso procede per confronti e scambi e l'algoritmo più veloce è quello che ovviamente ne necessita di meno. Esistono molti algoritmi di sort ma qui di seguito ne prenderemo in considerazione solo tre: bubble sort, selection sort e quick sort. NOTA: ti ricordo che dal sito puoi scaricare una videolezione con la simulazione animata dei tre tipi di sort. BUBBLE SORT Il vettore viene fatto scorrere da sinistra a destra (ordinamento crescente) tante volte quanti sono gli elementi meno uno (l'ultimo che avanza sarà per forza nella posizione che gli compete); e ad ogni passata un ciclo più interno confronta, sempre ripartendo dall'inizio, il primo elemento con il secondo e nel caso li scambia, il secondo con il terzo e nel caso li scambia e così via fermandosi ogni volta una posizione prima rispetto alla passata precedente (sequenza di confronti e scambi effettuata dal ciclo interno). In questo modo ad ogni passata il maggiore dei numeri rimasti viene spostato nella posizione più destra ancor da occupare emergendo come una bolla che procede verso la superficie (da cui il nome dell'algoritmo). void Bubblesort (int data[],int n) { int tmp,i,j; for (i=0; i<n-1; i++) { for (j=0; j<n-i-1; j++) if (data[j] > data[j+1]) { tmp = data[j]; data[j] = data[j+1]; data[j+1] = tmp; } } } Questo algoritmo è molto semplice da codificare ma ha prestazioni accettabili solo per piccoli vettori. Il numero di operazioni che compie (confronti più scambi) nel caso medio è nell'ordine di n2 dove n è il numero degli elementi del vettore. SELECTION SORT Procede per selezioni successive del minimo degli elementi che rimangono da ordinare. Alla prima passata determina la posizione del minimo valore cercandolo in tutto il vettore; il minimo è quindi messo in prima posizione ed il suo posto preso da quello che era in prima posizione. Poi cerca il minimo elemento dalla seconda posizione all'ultima: il minimo viene poi messo in seconda posizione. Si procede così fino che non si rimane con l'ultimo elemento che per forza di cose è già al suo posto. void selectionSort(int v[], int num_ele) { int temp=0; for (int i=0; i<num_ele-1; i++) { int posMin = posMinimo(v, i, num_ele-1); temp=v[i]; v[i]=v[posMin]; v[posMin] = temp; }} Il numero di operazioni che compie (confronti più scambi) nel caso medio è nell'ordine di n2 dove n è il numero degli elementi del vettore. QUICK SORT È ancora oggi considerato probabilmente il più veloce ma è anche instabile (in alcune situazioni le sue prestazioni possono decadere a livello dei peggiori algoritmi di sort). Inizialmente viene scelto un elemento che divide, rispetto la sua posizione, il vettore in due parti (chiamiamo questo elemento pivot, perno); con un ciclo procede poi a cercare partendo da sinistra il primo elemento di quella metà che ha un valore maggiore del perno (che dovrebbe quindi nell'ordine stare alla destra del perno e non alla sua sinistra come ora); poi si cerca partendo dal fondo il primo elemento nella parte di destra per valore dovrebbe stare alla sinistra del pivot; i due elementi trovati vengono scambiati. Se uno degli elementi (a sinistra o destra) non viene trovato lo scambio avviene con il pivot. Si procede così fino a che la ricerca nella a parte a sinistra scavalca la ricerca nella parte a destra. Dopo questa prima passata alla sinistra del pivot avrò tutti elementi più piccoli (non li avrei spostati lì) di quelli quelli che si trovano alla destra del pivot. Il meccanismo si ripete lavorando separatamente sulla parte di sinistra e quella di destra considerati come due vettori più piccoli da ordinare. Il meccanismo è di tipo ricorsivo ed infatti l'implementazione del quick sort è di solito sotto forma di una funzione ricorsiva. Idealmente il pivot dovrebbe sempre avere un valore che è medio rispetto a quelli che si trovano nella metà di sinistra e di destra. Nella realtà questo non avverrà sempre e se venisse scelto 'male' per com'è la posizione degli elementi di partenza si avrebbe, come detto, un notevole scadimento delle prestazioni. Notiamo come rispetto al bubble sort dove gli scambi avvengono tra elementi adiacenti facendo percorrere loro piccoli spostamenti di una posizione per volta nel quick sort con una singola operazione di scambio possono essere spostati due elementi che stanno agli opposti del vettore. void quick_sort(int arr[], int low, int high) { int i = low; int j = high; int y = 0; /* compare value */ int z = arr[(low + high) / 2]; /* partition */ do { /* find member above ... */ while(arr[i] < z) i++; /* find element below ... */ while(arr[j] > z) j--; if(i <= j) { /* swap two elements */ y = arr[i]; arr[i] = arr[j]; arr[j] = y; i++; j--; } } while(i <= j); /* recurse */ if(low < j) quick_sort(arr, low, j); if(i < high) quick_sort(arr, i, high); } Il numero di operazioni che compie (confronti più scambi) nel caso medio è nell'ordine di Nlog(N) dove N è il numero degli elementi del vettore.