Informatica GLI ALGORITMI DI ORDINAMENTO Un algoritmo si può definire come un procedimento che consente di ottenere un risultato atteso eseguendo, in un determinato ordine, un insieme di passi semplici corrispondenti ad azioni scelte solitamente da un insieme finito. Da questa definizione si evincono le quattro proprietà fondamentali dell'algoritmo: la sequenza di istruzioni deve essere finita (finitezza); essa deve portare ad un risultato (effettività); le istruzioni devono essere eseguibili materialmente (realizzabilità); le istruzioni devono essere espresse in modo non ambiguo (non ambiguità). Un algoritmo di ordinamento è un algoritmo che viene utilizzato per elencare gli elementi di un insieme secondo una sequenza stabilita da una relazione d'ordine, in modo che ogni elemento sia minore (o maggiore) di quello che lo segue. In assenza di altre specifiche, la relazione d'ordine viene sempre considerata totale (cioè tale da rendere sempre possibile il confronto tra due elementi dell'insieme). A seconda del verso della relazione considerato, un ordinamento può essere ascendente o discendente. Per analizzare e studiare gli algoritmi di ordinamento sono stati definiti differenti criteri di partizionamento, ne descrivo i più importanti. Ordinamento interno e ordinamento esterno Se il file da ordinare, o la struttura dati, può essere contenuto in memoria, il metodo viene detto interno. L'ordinamento di file residenti su disco o su nastro viene chiamato ordinamento esterno: la differenza principale tra i due tipi di ordinamento sta nel fatto che mentre nel primo è possibile accedere direttamente a un record, nel secondo i record devono essere indirizzati in modo sequenziale o al più per grandi blocchi. Ordinamento per confronti-scambi e digitale A seconda del tipo di operazione che viene effettuata, si hanno due differenti tipi di ordinamento. L'ordinamento che effettua confronti e scambi e l'algoritmo digitale che accede all'informazione tramite un gruppo di bit alla volta. Ordinamento adattivo Solitamente un algoritmo di ordinamento sfrutta operazioni di confronto e scambio. Se tali operazioni vengono svolte in modo indipendente dai dati di input l'algoritmo viene definito non adattivo. Mentre se un metodo di ordinamento esegue diverse sequenze di operazioni in funzione del risultato dei confronti si ha un algoritmo adattivo. Descrivo alcuni importanti algoritmi di ordinamento: -Selection Sort -Insertion Sort -Quick Sort -Merge Sort -Bubble Sort -Counting Sort Insertion Sort Si assume che la sequenza da ordinare sia partizionata in una sottosequenza già ordinata, all'inizio composta da un solo elemento, e una ancora da ordinare. Alla k-esima iterazione, la sequenza già ordinata contiene k elementi. In ogni iterazione, viene rimosso un elemento dalla sottosequenza non ordinata e inserito nella posizione corretta della sottosequenza ordinata, estendendola così di un elemento. 3 2 5 1 4 considero il 2 2 3 5 1 4 considero il 5 2 3 5 1 4 considero il 1 1 2 3 5 4 considero il 4 12345 Selection Sort L'algoritmo seleziona di volta in volta il numero minore nella sequenza di partenza e lo sposta nella sequenza ordinata; di fatto la sequenza viene suddivisa in due parti: la sottosequenza ordinata, che occupa le prime posizioni dell'array, e la sottosequenza da ordinare, che costituisce la parte restante dell'array. Dovendo ordinare un array A di lunghezza n, si fa scorrere l'indice i da 1 a n-1 ripetendo i seguenti passi: si cerca il più piccolo elemento della sottosequenza A[i..n]; si scambia questo elemento con l'elemento i-esimo. Quick Sort Quicksort è un algoritmo di ordinamento ricorsivo che, come merge sort, si basa sul paradigma divide et impera. La 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. L'idea base può esprimersi agevolmente in termini ricorsivi. Ad ogni stadio si effettua un ordinamento parziale di una sequenza di oggetti da ordinare. Assunto un elemento come perno dello stadio, si confrontano con esso gli altri elementi e si posizionano alla sua sinistra i minori e a destra i maggiori, senza tener conto del loro ordine. Dopo questo stadio si ha che il perno è nella sua posizione definitiva. Esempio: 6 2 7 1 5 4 3 pivot 6 2154367 pivot 2 1254367 pivot 5 1243567 pivot 4 1234567 Merge Sort L'algoritmo funziona nel seguente modo: Se la sequenza da ordinare ha lunghezza 0 oppure 1, è già ordinata. Altrimenti: La sequenza viene divisa (divide) in due metà (se la sequenza contiene un numero dispari di elementi, viene divisa in due sottosequenze di cui la prima ha un elemento in più della seconda) Ognuna di queste sottosequenze viene ordinata, applicando ricorsivamente l'algoritmo(impera) Le due sottosequenze ordinate vengono fuse (combina). Per fare questo, si estrae ripetutamente il minimo delle due sottosequenze e lo si pone nella sequenza in uscita, che risulterà ordinata Esempio: [10 3 15 2 1 4 9 0] [10 3 15 2] [1 4 9 0] [10 3] [15 2] [1 4] [9 0] [3 10] [2 15] [1 4] [0 9] [2 3 10 15] [0 1 4 9] [0 1 2 3 4 9 10 15] Bubble Sort Il bubble sort è un algoritmo iterativo, ossia basato sulla ripetizione di un procedimento fondamentale. La singola iterazione dell'algoritmo prevede che gli elementi dell'array siano confrontati a due a due, procedendo in un verso stabilito (che si scorra l'array a partire dall'inizio in avanti, o a partire dal fondo all'indietro, è irrilevante; nel seguito ipotizzeremo che lo si scorra partendo dall'inizio). Per esempio, saranno confrontati il primo e il secondo elemento, poi il secondo e il terzo, poi il terzo e il quarto, e così via fino al confronto fra il penultimo e l'ultimo elemento. Ad ogni confronto, se i due elementi confrontati non sono ordinati essi vengono scambiati. Durante ogni iterazione almeno un valore viene spostato rapidamente fino a raggiungere la sua collocazione definitiva; in particolare, alla prima iterazione il numero più grande raggiunge l'ultima Esempio : 15 6 4 10 11 2 6 15 4 10 11 2 6 4 15 10 11 2 6 4 10 15 11 2 6 4 10 11 15 2 6 4 10 11 2 15 4 6 10 11 2 15 4 6 10 2 11 15 4 6 2 10 11 15 4 2 6 10 11 15 2 4 6 10 11 15 Heap Sort L'algoritmo che ordina in senso crescente inizia creando uno heap decrescente. Per ogni iterazione si copia la radice (primo elemento dell'array) in fondo all'array stesso, eseguendo uno scambio di elementi. L'algoritmo poi ricostruisce uno heap di elementi spostando verso il basso la nuova radice, e ricomincia con un altro scambio (tra il primo elemento dell'array e quello in posizione ), eseguendo un ciclo che considera array di dimensione progressivamente decrescente. Counting sort L'algoritmo conta il numero di occorrenze di ciascun valore presente nell'array da ordinare, memorizzando questa informazione in un array temporaneo di dimensione pari all'intervallo di valori. Il numero di ripetizioni dei valori inferiori indica la posizione del valore immediatamente successivo. Si calcolano i valori massimo e minimo dell'array e si prepara un array ausiliario C di dimensione pari all'intervallo dei valori con C[i] che rappresenta la frequenza dell'elemento 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 .