Sei sulla pagina 1di 29

Algoritmi e strutture dati

Progetto per la sessione invernale 2010/2011


Universit degli Studi di Urbino Carlo Bo Facolt di Scienze e Tecnologie Corso di Laurea in Informatica Applicata Percorso On-line

Proposto da: Alberto Arvizzigno Matricola n. 237953 E-mail: arvizzignoa@simail.it

Progetto Algoritmi e Strutture Dati

1. Specifica del problema.


Si supponga di elaborare i dati riguardanti la rete di distribuzione di unazienda. Tale rete `e rappresentata tramite una matrice il cui generico elemento, identificato da riga i e colonna j, rappresenta il costo in Euro per trasportare merce direttamente tra 2 sedi di distribuzione dei prodotti (le sedi i e j, rispettivamente). Qualora non sia possibile trasportare merce direttamente da una sede ad unaltra si assume che il costo associato al trasporto su quel tratto sia infinito. Scrivere un programma ANSI C che esegue le seguenti elaborazioni: 1. Acquisisce da file la matrice dei costi di trasporto diretti tra tutte le possibili sedi. 2. Calcola i costi minimi di trasporto (e il relativo percorso attraverso eventuali sedi intermedie) tra ogni possibile coppia di sedi. 3. Ordina le coppie di sedi in ordine crescente di costo di trasporto e stampa in standard output il risultato di tale ordinamento (ogni riga di output deve contenere la citt di partenza, quella di arrivo e il costo di trasporto.)

2. Analisi del problema


Al fine di rendere il programma realistico sono state assunte alcune condizioni di base attinenti linput acquisito da file quali: - Il costo minimo di collegamento diretto tra due sedi un valore sempre positivo, in quanto per potersi spostare da una sede allaltra, non sarebbe realistico pensare di avere una spesa negativa oppure nulla. Il programma potrebbe accettare anche numeri negativi, poich lalgoritmo di Floyd-Warshall impiegato per la risoluzione, ne presuppone limpiego, tuttavia in questo caso dovrebbe essere ritrattato lutilizzo del valore di INFINITO impostato a -1, assegnandogli ad esempio un valore molto alto. La matrice delle distanze acquisita da file pu quindi comprendere tre tipologie di valori, il valore 0 ma solo sulla diagonale, il valore -1, in assenza di collegamento diretto tra due sedi oppure un valore positivo e maggiore di 0 quando il collegamento esiste. - Il costo di collegamento tra due sedi uguale per entrambe le direzioni di movimento, ossia quello che spendo per andare dalla sede A alla sede B lo stesso che spendo per andare dalla sede B alla sede A. Il grafo risultante quindi non orientato, infatti un arco non orientato viene rappresentato per mezzo di una coppia di archi orientati. Questo implica che il programma genera inizialmente una matrice formata da n sedi estrapolate dal file in cui il costo diretto [i][j] uguale al costo diretto [j][i], pertanto la matrice simmetrica, questo genera inoltre anche simmetria nella matrice costi minimi come risultato finale dellalgoritmo di Floyd-Warshall, ed un elenco finale di coppie e distanze leggibile anche soltanto dai valori al di sopra, o rispettivamente al di sotto, della diagonale della matrice. - la matrice viene acquisita dal programma sulla base dellinput fornito da file, tale input ho supposto che abbia nel file in ingresso la seguente forma espressa su ogni riga: sedeA sedeB costo dove: sedeA e sedeB rappresentano i nomi delle sedi collegate da un percorso diretto, mentre costo il valore del costo diretto necessario per andare da una sede allaltra. L'output costituito dall'elenco, in ordine crescente, dei costi minimi tra tutte le sedi possibili. Se due sedi non sono collegate da un cammino il costo infinito e non risulta in elenco.

Pag. 2

Progetto Algoritmi e Strutture Dati

Non viene considerato neppure un costo di trasporto 0 esistente da una sede con se stessa nel calcolo della soluzione finale. Loutput viene pertanto espresso nella seguente forma per ogni coppia valida riscontrata: Il collegamento pi breve tra |sedeA| e |sedeB| ha costo |XXX| - sedeA e sedeB rappresentano i nomi delle sedi collegate da un cammino minimo, che potrebbe anche passare per altre sedi. - XXX rappresenta il costo minimo che unisce le due sedi. La forma in cui espresso loutput comunque una variante di quanto richiesto dalle specifiche, in cui viene supposto lutilizzo di una partenza e di una destinazione legate da un costo. Visto luso di un grafo non orientato (dettagliato meglio in seguito) in questo caso, si parla piuttosto di un collegamento minimo tra due punti in cui sorgente e destinazione sono interscambiabili, che quello che poi si evince dal formato di riga di standard output. La relazione che sfruttata tra lacquisizione dellinput e la generazione delloutput nasce dallimplementazione dellalgoritmo di Floyd-Warshall per la generazione di tutte le distanze minime possibili, lo stesso algoritmo inoltre genera la matrice dei padri grazie alla quale possibile determinare i cammini intermedi per ogni coppia, che non vengono comunque stampati in output in quanto non richiesti da specifiche. Loutput dei costi espresso in ordine crescente di costo tramite una funzione di stampa valori.

3. Progettazione dellalgoritmo
Il programma costituito da 3 sezioni principali: 1) Acquisizione dei dati da file e definizione di una struttura a grafo per il contenimento delle informazioni necessarie quali: nomi delle sedi e costi diretti di trasporto. 2) Attraversamento del grafo per la lettura e linserimento dei dati nella matrice di lavoro, successiva applicazione dellalgoritmo di Floy-Warshall per il calcolo delle distanze minime e della matrice dei padri in cui vengono determinati i punti intermedi del percorso minimo tra due sedi. 3) Generazione di un array dinamico di strutture definito da tutti i valori validi e non infiniti ricavati dalla matrice delle distanze, tale array sar poi ordinato utilizzando il costo minimo come parametro chiave di ordinamento in quanto richiesto da specifiche, in ordine crescente. Per quanto riguarda la gestione degli errori di memoria che potrebbero essere causati dalla funzione malloc, ho preferito interrompere il programma con listruzione exit (EXIT_FAILURE). Listruzione exit(), secondo le General information about the exam da evitare per il mantenimento dei principi della programmazione strutturata. Comunque un errore derivante da uno spazio di memoria non allocato correttamente non avrebbe portato a buon fine la corretta esecuzione del programma, quindi in questo caso ho preferito andare in deroga a tale regola. Analizziamo pi in dettaglio le tre sezioni in cui stato diviso il programma: - Sezione 1: i dati vengono letti dal file il cui nome acquisito come parametro dal programma stesso, ogni riga contiene il collegamento diretto tra due sedi. La struttura di memoria che ho scelto di utilizzare per il contenimento di tutti i dati un grafo la cui rappresentazione avviene tramite una struttura dati dinamica a lista di adiacenza, i vertici della lista sono le sedi, in cui oltre al nome viene salvato anche un numero di successione, mentre gli archi di collegamento sono i costi diretti tra i vertici. Le sedi, per poter essere inserite nella lista di adiacenza, vengono analizzate a coppie dalla

Pag. 3

Progetto Algoritmi e Strutture Dati

funzione controlla_agigungi_sedi() in modo che nella stessa funzione si possa aggiungere anche larco che le collega con il peso del costo diretto acquisito in input. Dal grafo, contando il numero di vertici, so quante sedi differenti esistono, il dato non , infatti, noto a priori e servir per allocare dinamicamente la matrice delle distanze dirette richiesta da specifiche, nonch tutte le altre impiegate per il calcolo. Il numero di successione assegnato ad ogni sede e quindi ad ogni vertice del grafo, servir da puntatore per poter interagire con la matrice delle distanze, in quanto la posizione [i][j] sulla matrice indentificher anche i nomi delle sedi con il numero di successione i e j. La scelta del grafo come struttura di contenimento iniziale delle informazioni del file serve anche per evitare successivi accessi al disco. Poich lesistenza o no di un nome di sede deve essere controllata singolarmente, la problematica potrebbe essere risolta o con una continua scansione dei dati del file oppure con una continua scansione dei dati salvati in memoria tramite il grafo, con questultima sicuramente pi efficiente. I dati di ogni nuova sede trovata non sono inseriti con alcun criterio di ordinamento nella lista di adiacenza, linserimento serve solo per tenere traccia dei nomi di sede associandogli un indice, quindi ogni nuovo elemento assume un numero dindice incrementato di 1 rispetto al precedente. - Sezione 2: la lista di adiacenza creata mi fornisce alcuni dati utili, infatti, i vertici indicheranno il numero totale di sedi differenti che devo tenere in considerazione, il dato, come gi detto nella Sezione 1, viene utilizzato per allocare dinamicamente alcune matrici: Matrice di lavoro (ossia la matrice di adiacenza derivante dalla lista di adiacenza delle sedi). Matrice delle distanze, in cui verranno inseriti i dati calcolati con lalgoritmo di Floyd-Warshall. Matrice dei padri, che contiene il percorso relativo di passaggio per andare da una sede ad unaltra, questo anche il risultato richiesto dal punto due delle specifiche. La matrice di lavoro inizialmente settata con tutti i valori a infinito e la diagonale a 0 che infatti contiene il costo di ogni sede con se stessa, tramite unapposita funzione. Le altre due matrici, non vengono settate da nessuna funzione specifica ma la prima parte dellalgoritmo di Floyd-Warshall dedicato a tale scopo. Viene a questo punto attraversata la lista di adiacenza, ogni vertice analizzato sequenzialmente e per ogni vertice sono analizzati gli archi uscenti. I dati forniti dalloperazione saranno: il numero si successione associato al nome della sede da usare come indice per la matrice dei costi diretti; il collegamento tra la sede sorgente e la sede di destinazione, di cui posso leggere il numero di successione, che mi fornir il secondo indice da utilizzare con la matrice; il valore del peso salvato nellarco, ossia la distanza diretta tra due sedi. Una volta definita la matrice avente dimensione |n^2|, dove n il numero di vertici del grafo, viene applicato lalgoritmo di Floyd-Warshall, opportunamente modificato per poter trattare il valore infinito. Lalgoritmo di Floyd-Warshall si basa sulla soluzione del problema della chiusura transitiva di un grafo diretto, ovvero del problema di determinare lesistenza di un percorso per ogni coppia di vertici. Basandosi sul principio dellalgoritmo per la chiusura transitiva determina dist(i,j)(k) = min(dist(i,j)(k-1) , dist(i,k)(k-1) dist(k,j)(k-1)) calcolando i costi minimi tra due vertici analizzando tutti i possibili costi intermedi. Oltre a restituire come risultato le due matrici delle distanze minime e dei padri, restituisce, tramite la variabile locale contacoppie, il numero di collegamenti tra tutti i possibili vertici, che non abbiano una distanza infinita. Il dato serve per allocare correttamente il vettore di strutture che conterr lelenco delle coppie di percorso minimo e rappresenta il numero di linee delloutput finale.

Pag. 4

Progetto Algoritmi e Strutture Dati

I dati numerici in termini di costo saranno poi estrapolati attraversando tutti gli elementi dalla matrice delle distanze, mentre i dati alfanumerici che identificano il nome della sede verranno estrapolati dalla lista di adiacenza grazie al numero di successione usato come indice. Come specificato anche nella sezione di analisi del problema, avendo considerato gli archi per andare da una sede allaltra a coppie simmetriche, generato un grafo non orientato che a sua volta genera una matrice delle distanze minime simmetrica, quindi solo la met dei dati contenuti nella matrice di pratica utilit e solo la met delle coppie sar inserita nel vettore di strutture poich laltra met contiene valori identici. Se il grafo fortemente connesso, in cui per ogni coppia di nodi v1 e v2 si ha che v1 connesso a v2, cio esiste sempre un cammino da v1 a v2, allora il numero n di vertici totali genera n^2 coppie di distanze minime in una matrice di Floyd-Warshall, tuttavia quello che dovr elaborare sar (n^2-n) / 2 , in quanto dovr scartare la diagonale che indica semplicemente che ogni elemento ha un costo nullo con se stesso, mentre del risultato, per simmetria prender solo met dei valori calcolati. Il test nr.9 descritto nel paragrafo di testing del programma si proporr di verificare quanto affermato. - Sezione 3: dopo aver allocato il vettore di strutture possibile copiare i dati dalla matrice delle distanze generata da Floyd-Warshall nel vettore stesso, la struttura stata creata in modo da poter contenere le due stringhe di caratteri dei nominativi delle sedi nonch il costo collegamento. I dati sono inseriti nel vettore dinamico di dimensione t in modo non ordinato e per poter raggiungere loutput richiesto viene applicato lalgoritmo di ordinamento heapsort al vettore, utilizzando come parametro il costo cresciente. Il vettore, dopo essere stato ordinato, viene stampato in una variante rispetto a quella richiesta da specifiche e determinata dalla struttura simmetrica della matrice, si nota che per rispettare la forma richiesta da specifiche sarebbe bastato stampare due volte la stessa stringa invertendo sorgente e destinazione. La scelta di un algoritmo di tipo heapsort deriva dalle sue stesse caratteristiche, in quanto lalgoritmo opera sul posto non richiedendo altra memoria, il ch in caso di grosse quantit di dati sarebbe gi intensivamente utilizzata per lallocazione delle tre matrici e della lista di adiacenza; infine lalgoritmo non risente del problema di degrado quadratico che avrei potuto ottenere nel caso peggiore usando lalgoritmo di quicksort. La complessit asintotica risultante dallordinamento, dato un numero di elementi t in input, dove t il numero di cammini totali che si possono verificare, non dipenderebbe dallo stato iniziale degli elementi e sarebbe pari a O(t*log t). Dato che t nel caso peggiore uguale a ((n^2)-n)/2, in quanto rapportato al numero di vertici n presi in input, il valore degli elementi della matrice simmetrica quadrata senza la diagonale, il costo dellheapsort nel caso pessimo, espresso in funzione degli n vertici anzich del numero t di cammini diventa O(n^2 * log n^2). Questo valore andrebbe a sommarsi ad una complessit lineare pari a O(n^2) derivante dallinserimento dei dati nel vettore ottenuti tramite lattraversamento della matrice[n][n]. Per lo studio della complessit completa del programma si rimanda allultimo paragrafo. I file di libreria scritti matrice.c e fw-odinamento.c, contengono le funzioni necessarie alla risoluzione dei sottoproblemi incontrati. In particolare il file matrice.c contiene le funzioni necessarie alla risoluzione della prima sezione, mentre il file fw-ordinamento contiene le funzioni necessarie alla risoluzione delle ultime due sezioni descritte, come limplementazione dellalgoritmo di Floyd- Warshall e dellalgoritmo di heapsort per lordinamento dei costi minimi tra tutte le coppie di sedi.

Pag. 5

Progetto Algoritmi e Strutture Dati

4. Implementazione dellalgoritmo
I file seguenti sono stati compilati per il testing con: gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3). - nome file: mincost.c
/**********************************************************/ /* Programma per la ricerca dei percorsi minimi tra tutte */ /* le coppie di destinazioni valide che non risultino a */ /* consto infinito. */ /* Il programma, una volta identificati i cammini minimi */ /* li stampa in ordine crescente. */ /* Autore: Alberto Arvizzigno */ /* arvizzignoa@simail.it */ /* Matricola n. 237953 */ /* Data rilascio: 30 gennaio 2012 */ /**********************************************************/ #include #include #include #include <stdio.h> <stdlib.h> "matrice.h" "fw-ordinamento.h"

int main( int argc, char *argv[]) { /*---------------definizione variabili-----------------*/ char destinazione_a[MAXCHAR], destinazione_b[MAXCHAR]; float costo; int contatore_dim_matrice, contacoppie; float **matrice_lavoro=NULL , **distanze=NULL , **padri=NULL; sede_t *lista_sedi=NULL; struct lista_costi *lista_costi_coppie=NULL; /*--------------fine definizione variabili-------------*/ printf("\nApertura file: %s\n",argv[1]); FILE *file_dati = fopen ( argv[1],"r"); /* apertura file in lettura da argomento file */ if ( file_dati!= NULL) /*se il file dati valido */ { while (!feof(file_dati) { if (fscanf(file_dati, "%s %s %f", destinazione_a, destinazione_b , &costo) != EOF) /*EOF definito in stdio.h*/ /*lettura elementi salvati su file per compilazione matrice*/ /*gli elementi sono salvati nella forma: sedeA sedeB distanza */ /*se non siamo arrivati ancora alla fine del file allora contolla gli*/ /*elmenti dello stream*/ lista_sedi= controlla_aggiungi_sedi ( &contatore_dim_matrice, lista_sedi, destinazione_a , destinazione_b, costo); /*se la linea di file non vuota o invalida i dati letti vengono processati ed inseriti in una struttura a grafo */ }/* fine ciclo while fine lettura dati da file*/ /*--------------------------------------------------------*/ /* ALLOCAZIONE MEMORIA MATRICI NECESSARIE AL CALCOLO */ /* l'allocazione avviene dinamicamente */ /*--------------------------------------------------------*/ matrice_lavoro =alloca_matrice_nuovo_elemento(contatore_dim_matrice,matrice_lavoro); /*alloca la matrice di lavoro*/ distanze=alloca_matrice_nuovo_elemento(contatore_dim_matrice,distanze); /*alloca matrice distanze per algoritmo floydwarshall*/ padri=alloca_matrice_nuovo_elemento(contatore_dim_matrice,padri); /*alloca matrice dei padri per algoritmo floydwarshall*/ /*----------FINE BLOCCO ALLOCAZIONE MATRICI----------*/ setta_matrice(matrice_lavoro,INFINITO,contatore_dim_matrice); /*setting della matrice principale*/ /*tutti valori sono = INFINITO */ /*tranne la diagonale settata a 0*/

Pag. 6

Progetto Algoritmi e Strutture Dati

crea_matrice_da_grafo(matrice_lavoro,lista_sedi); /*salvataggio dei dati prelevati dal file nella matrice di lavoro*/ /* considerando che il costo per andare da A a B uguale al costo per andare da B ad A la matrice risulta simmetrica con la diagonale =0 */ contacoppie = floyd_warshall(matrice_lavoro, distanze, padri, contatore_dim_matrice); /*calcolo della matrice delle distanze e della*/ /*matrice dei padri contenente tutte le posizioni intermedie.*/ /*La funzione restituisce il numero di coppie con distanza non nulla*/ /*che sono state trovate, serve per l'allocazione dell'array*/ /*di strutture e per la scrittura del risultato finale*/ lista_costi_coppie=genera_array_distanze_minime(distanze,contatore_dim_matrice,contacoppie,lista_s edi); /*inserimento dati della matrice di floyd-warshall nell'array di strutture */ /*contente tutte le coppie collegate con la distanza minima di collegamento*/ heapsort (lista_costi_coppie , contacoppie); /*utilizzo dell'heapsort per l'ordinamento*/ /*dell'array di strutture contente le coppie ed il costo minimo*/ stampa_lista_coppie (lista_costi_coppie, contacoppie); /*stampa risultati sedeA, sedeB, costo */ } else /*errore se il file dati non valido */ { printf ("\nInserire come parametro il nome file corretto da processare\n"); exit (EXIT_FAILURE); /* implementata in stdlib.h*/ }

/*libero la memoria occupata matrici liste e array di struttura*/ rilascia_matrice(matrice_lavoro,contatore_dim_matrice); rilascia_matrice(distanze,contatore_dim_matrice); rilascia_matrice(padri,contatore_dim_matrice); rilascia_grafo (lista_sedi, contatore_dim_matrice); rilascia_vett_strutt (lista_costi_coppie, contacoppie); return 0; /*ritorno main */ }

- nome file: matrice.h


/*-----------------------------------------------------------*/ /* file: matrice.h */ /* File d'intestazione delle funzioni necessarie per la */ /* creazione di una struttura a lista di adiacenza, */ /* allocazione dinamica di una matrice */ /* conversione lista di adiacenza in matrice di adiacenza */ /* Autore: Alberto Arvizzigno */ /* arvizzignoa@simail.it */ /* Matricola n. 237953 */ /*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/ /* dichiarazione della struttura per la lista di adiacenza */ /* sar usata per la costruzione della matrice di adiacenza */ /* da usare come input per l'algoritmo di floyd-warshall */ /*-----------------------------------------------------------*/ typedef struct sede { char *nome_sede; int numero_successione; struct sede *succ_p; struct peso *collega_pesi; } sede_t; /* struttura archi appartente alle sedi */ typedef struct peso { float peso; struct peso *peso_successivo; struct sede *sede_dst; } peso_t; /*--------------------------------------*/

Pag. 7

Progetto Algoritmi e Strutture Dati

/*-----------------------------------------*/ /*ridichiarazione delle funzioni esportate */ /*-----------------------------------------*/ extern sede_t *controlla_aggiungi_sedi (int *, sede_t *, char *, char *, float ); extern float **alloca_matrice_nuovo_elemento (int , float **); extern void crea_matrice_da_grafo ( float **, sede_t * ); extern void rilascia_matrice (float **, int ); extern void rilascia_grafo ( sede_t* , int); /*-----------------------------------------*/

- nome file: matrice.c


/*-----------------------------------------------------------*/ /* file: matrice.c */ /* File d'implementazione delle funzioni necessarie per la */ /* creazione di una struttura a lista di adiacenza, */ /* allocazione dinamica di una matrice */ /* conversione lista di adiacenza in matrice di adiacenza */ /* Autore: Alberto Arvizzigno */ /* arvizzignoa@simail.it */ /* Matricola n. 237953 */ /*-----------------------------------------------------------*/ /****************************/ /* dichiarazione librerie */ /****************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "matrice.h" /*--------fine ----------- */

/*-----------------------------------*/ /* dichiarazione funzioni utilizzate */ /*-----------------------------------*/ sede_t *controlla_aggiungi_sedi (int *, sede_t *, char *, char *, float ); sede_t* aggiungi_elemento ( sede_t *, char *, int *); void aggiungi_arco ( sede_t *, sede_t *, float); sede_t* cerca_elemento_lista (sede_t *,char *); float **alloca_matrice_nuovo_elemento (int , float **); void crea_matrice_da_grafo ( float **, sede_t * ); void rilascia_matrice (float **, int ); void rilascia_grafo ( sede_t* , int); /*----------------------------------------------------------------------------------------------*/ /* controlla l'esistenza della coppia di destinazioni se non esiste l'aggiunge e gli assegna un numero di successione*/ /*----------------------------------------------------------------------------------------------*/ sede_t* controlla_aggiungi_sedi (int *numero_succ, sede_t *sedi, char *nome_sedeA , char *nome_sedeB, float peso_costo) { sede_t *nuovasede=sedi, *sede_appoggioA , *sede_appoggioB , *src , *dst; if ( (strcmp(nome_sedeA, nome_sedeB)!=0) && (peso_costo>0) ) /*effettua alcuni controlli sui valori proposti ,se il controllo non viene passato la coppia di valori viene scartata, la coppia immessa non pu essere sorgente==destinazionee il peso minimo della coppia deve essere maggiore di 0*/ { if ((sede_appoggioA=cerca_elemento_lista(sedi,nome_sedeA))==NULL) /*la sedeA nuova*/ { nuovasede= aggiungi_elemento (nuovasede,nome_sedeA , numero_succ); /*se nuova viene aggiunta alla lista delle sedi*/ src=nuovasede;/*se il nominativo nuovo allora diventa la sorgente*/ } else src=sede_appoggioA; /* se il nominativo gi presente in elenco allora la src resta il vecchio elemento */ if ((sede_appoggioB=cerca_elemento_lista(sedi,nome_sedeB))==NULL) /*la sedeB nuova */ {

Pag. 8

Progetto Algoritmi e Strutture Dati

nuovasede= aggiungi_elemento (nuovasede, nome_sedeB , numero_succ);/* se nuova viene aggiunta alla lista delle sedi */ dst=nuovasede; /*se il nominativo nuovo allora il nuovo nominativo diventa la destinazione*/ } else dst=sede_appoggioB;/*se il nominativo gi presente in elenco allora la src resta il vecchio elemento */ /* ATTENZIONE! IL COSTO PER ANDARE DA UNA SEDE ALL'ALTRA E' UGUALE IN ENTRAMBE LE DIREZIONI, QUESTO GENERA UNA MATRICE DI COSTI SIMMETRICA*/ /*poich sorgente e destinazione sono percorribili in entrambi i sensi vengono*/ /*aggiunti due archi di peso uguale uno dalla sorgente e uno dalla destinazione*/ aggiungi_arco(src,dst,peso_costo); /*inserimento costo da srd a dst */ aggiungi_arco(dst,src,peso_costo); /* inserimento costo da dst a src*/ } else /*se viene riscontato un errore il programma scarta il valore non considerandolo*/ printf ("\nTrovati valori errati, la coppia %s e %s con valore %.2f stata scartata\n",nome_sedeA,nome_sedeB,peso_costo); return nuovasede; } /*------------------fine funzione-----------------------------*/ /*-------------------------------------------*/ /*aggiunge l'elemento alla lista di adiacenza*/ /*l'elemento contiene il nome della sede */ /* ed il numero di successione attribuito */ /*-------------------------------------------*/ sede_t *aggiungi_elemento ( sede_t *lista_sedi, char *nome_sede, int *numero_successione) { /*dichiarazione variabili locali*/ sede_t *nuova_sede; nuova_sede = (sede_t *)malloc (sizeof(sede_t)); /*allocazione spazio struttura per inserimento vertice*/ nuova_sede->nome_sede = (char*)malloc(strlen(nome_sede)+1); /* allocazione spazio stringa infrastruttura*/ if ((nuova_sede == NULL) || (nuova_sede->nome_sede==NULL))/* in caso di errore*/ { printf ("Errore di allocazione in memoria dell'oggetto"); exit(EXIT_FAILURE); } nuova_sede->succ_p=NULL; strcpy ( nuova_sede->nome_sede , nome_sede );/* copia del nome della sede*/ nuova_sede->numero_successione = *numero_successione;/*numero di successione*/ nuova_sede->succ_p=lista_sedi;/*passa alla sede successiva*/ (*numero_successione)++;/*incremento del numero di succ per il prossimo inserimento*/ return nuova_sede; } /*------------fine funzione-------------*/ /*--------------------------------------------------------------*/ /*funzione per aggiungere un arco dinamicamente al nodo */ /*--------------------------------------------------------------*/ void aggiungi_arco ( sede_t *sede_list_src, sede_t *sede_list_dst, float peso) { /*dichiarazione variabili locali*/ peso_t *nuovo_arco=NULL; nuovo_arco= (peso_t *)malloc (sizeof(peso_t)); /* alloca arco da src a dst*/ if (nuovo_arco == NULL) /* se malloc restituisce NULL errore */ { printf ("Errore di allocazione oggetto in memoria"); exit(EXIT_FAILURE);

Pag. 9

Progetto Algoritmi e Strutture Dati

} /*assegna i valori che mi servono al nuovo arco tra cui il peso ( costo )*/ nuovo_arco->peso = peso; /* assegna il peso all'arco*/ nuovo_arco->sede_dst=sede_list_dst; nuovo_arco->peso_successivo=NULL; /*determino i puntatori del nuovo arco*/ if (sede_list_src->collega_pesi==NULL) sede_list_src->collega_pesi=nuovo_arco; else { nuovo_arco->peso_successivo=sede_list_src->collega_pesi->peso_successivo; sede_list_src->collega_pesi->peso_successivo=nuovo_arco; }} /*----------------------------------*/ /*-----------fine funzione----------*/ /*----------------------------------*/ /*---------------------------------------------------*/ /* Funzione di ricerca elemento */ /* ritorna l'elemento se l'elemento nella lista */ /* altrimenti NULL se non lo trova */ /*---------------------------------------------------*/ sede_t *cerca_elemento_lista (sede_t *nodo,char *nome_sede_cmp) { /*definizione variabili locali*/ sede_t *punta=nodo; /*---------------------------*/ for(punta=nodo; (punta!=NULL) ;punta=punta->succ_p) { /*se trova il valore esce subitop dal ciclo restituiendo la posizione nella lista*/ if (strcmp( punta->nome_sede, nome_sede_cmp)==0) return punta; } return punta; } /*-------------*/ /*fine funzione*/ /*-------------*/ /* --------------------------------------------------*/ /* alloca la matrice dinamica definita da n elementi */ /* --------------------------------------------------*/ float **alloca_matrice_nuovo_elemento (int num_elementi_matrice, float **matrice) { /*dichiarazione variabili locali*/ int i; /* allocazione matrice quadrata contenete num_elementi_matrice*/ matrice = (float **)malloc((num_elementi_matrice)*(sizeof (float*))); if (matrice == NULL) /* se malloc restituisce NULL errore */ { printf ("Errore di allocazione oggetto in memoria"); exit(EXIT_FAILURE); } for (i=0;i<num_elementi_matrice;i++) { matrice[i]=(float*)malloc(num_elementi_matrice*sizeof(float)); if (matrice[i] == NULL) /* se malloc restituisce NULL errore */ { printf ("Errore di allocazione oggetto in memoria"); exit(EXIT_FAILURE); }} return matrice; } /*-------------*/ /*fine funzione*/ /*-------------*/

Pag. 10

Progetto Algoritmi e Strutture Dati

/*------------------------------------------------*/ /*visita la lista di adiacenza per l'ottenimento */ /*della matrice di adiacenza */ /*------------------------------------------------*/ void crea_matrice_da_grafo ( float **matrice, sede_t *lista_sedi) { /*definizione variabili locali */ sede_t* punta=lista_sedi; peso_t *costi; /*-----------------*/ /* la crezione della matrice di adiacenza avviene attraversando sequenzialmente i vertici della lista di adiacenza e per ogni vertice vengono attraversati gli archi che ne fanno parte, durante l'attraversamento i dati vengono copiati nella matrice di adiacenza */ for (; punta!=NULL ;punta=punta->succ_p) { costi=punta->collega_pesi; for ( ;costi!=NULL ; costi= costi->peso_successivo) /*scorro la lista di adiacenza*/ matrice[punta->numero_successione][costi->sede_dst->numero_successione]=costi->peso;/*inserisco nella matrice il valore prelevando dati e indici dalla lista di adiacenza*/ }} /*----------fine funzione----------*/ /*--------------------------------------------------------*/ /*funzioni di rilascio memoria allocata per matrice e grafo*/ /*--------------------------------------------------------*/ void rilascia_matrice (float **matrice, int num_elementi_matrice) { int i; { for (i=0;i<num_elementi_matrice;i++) free(matrice[i]); } free(matrice); } /* attraverso il grafo eliminando archi e vertici */ void rilascia_grafo ( sede_t* nodo, int elementi) { for(; (nodo!=NULL) ;nodo=nodo->succ_p) { for(;(nodo->collega_pesi!=NULL); nodo->collega_pesi=nodo->collega_pesi->peso_successivo); free(nodo->collega_pesi); free(nodo); } } /*fine funzione*/

- nome file: fw-ordinamento.h


/*-----------------------------------------------------------*/ /* file: fw-ordinamento.h */ /* File d'intestazione delle funzioni necessarie per */ /* l'implementazione dell'algoritmo di floyd-warshall */ /* e per l'algoritmo heapsort di ordinamento del vettore */ /* funzioni di settaggio iniziale di matrice e di rilascio */ /* memoria */ /* Autore: Alberto Arvizzigno */ /* arvizzignoa@simail.it */ /* Matricola n. 237953 */ /*-----------------------------------------------------------*/ /* definizione costanti */ #define INFINITO -1 #define MAXCHAR 20 /*---------------------*/ /* struttura contenente i nomi delle sedi ed il costo minimo tra di esse*/ /* usata nella costruzione del vettore di strutture */ struct lista_costi

Pag. 11

Progetto Algoritmi e Strutture Dati

{ char *nome_sede_a; char *nome_sede_b; float costo; };

/*ridichiarazioni delle funzioni esportate specifiche per */ /*l'algoritomo di Floyd-Warshall e per l'ordinamento costi minimi*/ extern int floyd_warshall(float **, float **, float **, int ); extern void setta_matrice ( float **, int , int ); extern struct lista_costi* genera_array_distanze_minime( float **, int ,int, sede_t *); extern void stampa_lista_coppie (struct lista_costi[] , int); extern void heapsort (struct lista_costi [], int ); extern void rilascia_vett_strutt (struct lista_costi *, int);

- nome file: fw-ordinamento.c


/*-----------------------------------------------------------*/ /* file: fw-ordinamento.c */ /* File d'implementazione delle funzioni necessarie per */ /* l'implementazione dell'algoritmo di floyd-warshall */ /* e per l'algoritmo heapsort di ordinamento del vettore */ /* funzioni di settaggio iniziale di matrice e di rilascio */ /* memoria */ /* Autore: Alberto Arvizzigno */ /* arvizzignoa@simail.it */ /* Matricola n. 237953 */ /*-----------------------------------------------------------*/ #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> "matrice.h" "fw-ordinamento.h"

/*----------------------------------------------------*/ /*dichiarazioni di funzioni specifiche per l'algoritmo*/ /*di Floyd-Warshall e per l'ordinamento costi minimi */ /*----------------------------------------------------*/ int floyd_warshall(float **, float **, float **, int ); void setta_matrice ( float **, int , int ); struct lista_costi* genera_array_distanze_minime( float **, int ,int, sede_t *); void stampa_lista_coppie (struct lista_costi* , int); void heapsort (struct lista_costi*, int ); void sieve_heap (struct lista_costi*, int , int ); void rilascia_vett_strutt (struct lista_costi*, int); /*----------------------------------------------------*/ /*---------------------------------------------------------------*/ /*calcola la matrice delle distanze tra tutte le coppie di valori*/ /* la funzione con l'algoritmo di floyd warshall stata */ /* modificata per poter utlizzare -1 come valore infinito */ /* inoltre conta le coppie di valori con un cammino non INFINITO */ /*---------------------------------------------------------------*/ int floyd_warshall (float **matrice_lavoro, float **distanze, float **padri, int numero_vertici) { /*dichiarazione variabili locali*/ int i,j, k, contacoppie=0, precedente, inf=0; /*variabile inf serve per contare il numero di infiniti presenti*/ /*-----------------------------*/ /*implementazione dell'algoritmo di floy-warshall come da lezione numero 27 opportunamente modificato per la gestione del costo infinito rappresentato da INFINITO= -1*/ for (i = 0; (i < numero_vertici); i++) for (j = 0; (j < numero_vertici); j++) { distanze[i][j] = matrice_lavoro[i][j]; if (distanze[i][j]==INFINITO) inf++; /*quanti valori di INFINITO

Pag. 12

Progetto Algoritmi e Strutture Dati

c'erano prima del calcolo?*/ padri[i][j] = (matrice_lavoro[i][j] != INFINITO)? i: -1; } for (k = 0; (k < numero_vertici);k++) { for (i = 0; (i < numero_vertici);i++) { for (j = 0; (j < numero_vertici);j++) { precedente=distanze[i][j]; /*l'algoritmo di f-w prevede la possibilit di usare pesi negativi, tuttavia la costante INFINITO che viene usata = -1, quindi se da una parte utile per la facilit d'impiego dall'altra ho bisogno di inserire alcuni step di controllo in pi per verificare il caso INFINITO che non altro che un numero negativo e non deve essere messo a calcolo nella funzione.*/ if (((distanze[i][k]!=INFINITO) && (distanze[k][j]!=INFINITO)) && distanze[i][j]==INFINITO) /*tratta la logica del caso infinito */ { distanze[i][j] = distanze[i][k] + distanze[k][j]; padri[i][j] = padri[k][j]; } else if ((distanze[i][k]!=INFINITO) && (distanze[k][j]!=INFINITO)) /* tratta la logica del caso infinito*/ if (distanze[i][j] > distanze[i][k] + distanze[k][j]) /* se la distanza maggiore viene rimpiazzata con la minore della triangolazione */ { distanze[i][j] = distanze[i][k] + distanze[k][j]; padri[i][j] = padri[k][j]; } if ((precedente==INFINITO) && (distanze[i][j]!=INFINITO)) inf--;/*quante coppie INFINITO sono scomparse dopo aver applicato f-w?*/ }}} contacoppie=((numero_vertici)*(numero_vertici)-numero_vertici-inf)/2; /* il numero di coppie calcolato in questo modo*/ /*[(tutti i vertici)^2 - diagonale - n.ro coppie tot INF]/2 */ /*la matrice simmetrica quindi prendo solo met del valore*/ return contacoppie; } /*-------------fine funzione floyd-warshall----------------------*/ /*-------------------------------------------------------------*/ /*funzione di settaggio matrice imposta il valore iniziale ad */ /*INFINITO e lo 0 sulla diagonale */ /*-------------------------------------------------------------*/ void setta_matrice ( float **matrice, int valore_sett , int elementi) { int i,j; for(i = 0; i < elementi; i++) { for(j = 0; j < elementi; j++) if (j==i) matrice[j][j]=0; else matrice[j][i]=INFINITO; } } /*-------------------fine funzione------------------*/ /*--------------------------------------------------------------*/ /*estrapolazione delle distanze minime dalla matrice calcolata */ /*con floyd-warshall, inserimento dei valori estrapolati */ /*in una lista non ordinata */ /*--------------------------------------------------------------*/ struct lista_costi * genera_array_distanze_minime( float **matrice, int elementi, int contacoppie, sede_t *lista_sedi_supp)

Pag. 13

Progetto Algoritmi e Strutture Dati

{ /*variabili interno funzione*/ sede_t * lista_sedi_RIG, *lista_sedi_COL; struct lista_costi *indice; int i,j,k=1; /*---------------------------*/ contacoppie++;/* incrementa numero per dare spazio alla posizione 0 del vettore usata dall'heap*/ indice= (struct lista_costi*)malloc(contacoppie*sizeof(struct lista_costi)); /*alloca elemento array[0] di struttura per la funzione heap*/ /* il vettore[0] da tenere vuoto */ indice[0].nome_sede_a = (char*)malloc(MAXCHAR*sizeof(char)); indice[0].nome_sede_b = (char*)malloc(MAXCHAR*sizeof(char)); /*----------------------------------------------------------*/ for (i=0, lista_sedi_RIG=lista_sedi_supp; (i<elementi) ;i++, lista_sedi_RIG=lista_sedi_RIG>succ_p) /*scansione delle righe della matrice e della lista*/ { for (j=i+1, lista_sedi_COL=lista_sedi_RIG->succ_p; (j<elementi) ;j++, lista_sedi_COL=lista_sedi_COL->succ_p) /*scansione delle colonne della matrice e della lista*/ if(matrice[elementi-1-i][elementi-1-j]!=INFINITO)/* se l'elemento nella matrice non infinito alloca salvalo nel vettore*/ { indice[k].nome_sede_a = (char*)malloc((strlen(lista_sedi_RIG->nome_sede)+1));/*allocazione memoria stringa*/ indice[k].nome_sede_b = (char*)malloc((strlen(lista_sedi_COL->nome_sede)+1));/*allocazione memoria stringa*/ strcpy ( indice[k].nome_sede_a , lista_sedi_RIG->nome_sede );/*copia la stringa della sede dalla lista di adiacenza puntato dalla RIGA nell'elemento del vettore di struttre*/ strcpy ( indice[k].nome_sede_b , lista_sedi_COL->nome_sede );/*copia la stringa della sede dalla lista di adiacenza puntato dalla COLONNA nell'elemento del vettore di struttre*/ indice[k].costo= matrice[elementi-1-i][elementi-1-j]; k++; }} return indice; /* restituisce il vettore di strutture */ } /*---------------fine funzione--------------------*/ /* --------------------------------*/ /* stampa il vettore di strutture */ /*---------------------------------*/ void stampa_lista_coppie (struct lista_costi *indice, int contacoppie) { /*variabili interno funzione*/ int i; for (i=1;i<=contacoppie;i++) printf(" \n Il collegamento pi breve tra |%s| e |%s| ha costo |%.2f|\n", indice[i].nome_sede_a, indice[i].nome_sede_b, indice[i].costo); printf("\n|----------------fine lista raggiunta ------------------|\n"); /*-------fine funzione-------------*/ /*---------------------------------*/ /* heap sort per ordinamento lista */ /* la posizione index[0] vuota */ /* come da lezione nr.04.11 */ /*---------------------------------*/ void heapsort(struct lista_costi *index, int elementi) { int sinistra, destra; struct lista_costi tmp; tmp.nome_sede_a = (char*)malloc(MAXCHAR*sizeof(char)); tmp.nome_sede_b = (char*)malloc(MAXCHAR*sizeof(char));

Pag. 14

Progetto Algoritmi e Strutture Dati

/* trasformo l'array in heap */ for (sinistra = elementi / 2;(sinistra >= 1); sinistra--) sieve_heap(index, sinistra, elementi); for (destra = elementi;(destra > 1); destra--) { /* tmp = index[1] */ strcpy (tmp.nome_sede_a , index[1].nome_sede_a) ; strcpy (tmp.nome_sede_b , index[1].nome_sede_b) ; tmp.costo=index[1].costo; /*index[1]=index[destra]*/ strcpy (index[1].nome_sede_a , index[destra].nome_sede_a); strcpy (index[1].nome_sede_b , index[destra].nome_sede_b); index[1].costo=index[destra].costo; /*index[destra] = tmp;*/ strcpy (index[destra].nome_sede_a , tmp.nome_sede_a); strcpy (index[destra].nome_sede_b , tmp.nome_sede_b); index[destra].costo=tmp.costo; sieve_heap(index,1,destra-1); }} /*-------------------------------------------*/ /*---------------------------------------------*/ /*implementazione dell'algoritmo di heapsort */ /*opportumanete modificato per gestire un */ /*vettore di strutture come da lezione nr.04.11*/ /*---------------------------------------------*/ void sieve_heap(struct lista_costi *index , int sinistra, int destra) { int i, j; struct lista_costi nuovo; /*allocazione della struttura nuovo utile per gli scambi*/ nuovo.nome_sede_a = (char*)malloc(MAXCHAR*sizeof(char)); nuovo.nome_sede_b = (char*)malloc(MAXCHAR*sizeof(char)); for ( nuovo.costo = index[sinistra].costo, strcpy (nuovo.nome_sede_a , index[sinistra].nome_sede_a), strcpy (nuovo.nome_sede_b , index[sinistra].nome_sede_b), i=sinistra , j=2*i ; (j <= destra);)

{ if ((j < destra) && (index[j + 1].costo > index[j].costo)) j++; if (nuovo.costo < index[j].costo) { strcpy (index[i].nome_sede_a, index[j].nome_sede_a); strcpy (index[i].nome_sede_b, index[j].nome_sede_b); index[i].costo = index[j].costo; i = j; j = 2 * i; } else j = destra + 1; } if (i != sinistra) { strcpy (index[i].nome_sede_a, nuovo.nome_sede_a); strcpy (index[i].nome_sede_b, nuovo.nome_sede_b); index[i].costo = nuovo.costo; } } /*--------fine funzione-------------------------*/ /*--------------------------------------------*/ /*rilascia la memoria del vettore di strutture*/

Pag. 15

Progetto Algoritmi e Strutture Dati

/*--------------------------------------------*/ void rilascia_vett_strutt (struct lista_costi *index, int contacoppie) { int i=0; for (;i<=contacoppie;i++) { free (index[i].nome_sede_a); free (index[i].nome_sede_b); } free (index); } /*-----------------fine funzione-------------------*/

- nome file: Makefile


#makefile CC = gcc PARMAIN = -ansi -Wall -O -o PAROGG = -ansi -Wall -O -c LIBFILEC = matrice.c fw-ordinamento.c LIBFILEO = matrice.o fw-ordinamento.c OUT = mincost tutti: @echo @echo @echo $(CC) $(CC) $(CC)

Inizio compilazione files $(PAROGG) $(LIBFILEC) $(PAROGG) $(OUT).c $(PARMAIN) $(OUT) $(OUT).o $(LIBFILEO)

rimuovi: @echo @echo Pulizia file oggetto... @echo rm -f *.o

Pag. 16

Progetto Algoritmi e Strutture Dati

5. Testing del programma


Sono stati effettuati nr. 10 testi significativi utilizzando i files .txt presenti nel pacchetto. - Testi nr. 1: test basico dati in input file:test0.txt torino roma 100 torino genova 20 genova roma 70 ./mincost test0.txt Il collegamento pi breve tra |genova| e |torino| ha costo |20.00| Il collegamento pi breve tra |genova| e |roma| ha costo |70.00| Il collegamento pi breve tra |roma| e |torino| ha costo |90.00| |----------------fine lista raggiunta ------------------| Nota: la distanza tra Torino Roma risulta di 90E. passando per Genova, si quindi ridotto il costo diretto tra Roma e Torino in input di 100E ai 90E derivanti dal calcolo. -Test nr. 2: secondo testo basico dati in input file:test1.txt roma milano 300 milano torino 100 torino genova 80 genova savona 30 savona torino 150 ./mincost test1.txt Il collegamento pi breve tra |savona| e |genova| ha costo |30.00| Il collegamento pi breve tra |genova| e |torino| ha costo |80.00| Il collegamento pi breve tra |torino| e |milano| ha costo |100.00| Il collegamento pi breve tra |savona| e |torino| ha costo |110.00| Il collegamento pi breve tra |genova| e |milano| ha costo |180.00| Il collegamento pi breve tra |savona| e |milano| ha costo |210.00| Il collegamento pi breve tra |milano| e |roma| ha costo |300.00| Il collegamento pi breve tra |torino| e |roma| ha costo |400.00| Il collegamento pi breve tra |genova| e |roma| ha costo |480.00| Il collegamento pi breve tra |savona| e |roma| ha costo |510.00|

Pag. 17

Progetto Algoritmi e Strutture Dati

|----------------fine lista raggiunta ------------------| -Test nr. 3: errore di inserimento per distanza negativa dati in input file:test2.txt sedea sedeb 100 sedea sedec -10 ./mincost test2.txt Trovati valori errati, la coppia sedea e sedec con valore -10.00 stata scartata Il collegamento pi breve tra |sedeb| e |sedea| ha costo |100.00| |----------------fine lista raggiunta ------------------| Nota: il programma non inserisce la coppia in quanto il costo in euro negativo ma inserisce solo il dato valido. -Test nr. 4: prova centesimi, verifico solo loutput corretto con un dato in centesimi dati in input file:test3.txt sedea sedeb 1000.50 ./mincost test3.txt Il collegamento pi breve tra |sedeb| e |sedea| ha costo |1000.50| |----------------fine lista raggiunta ------------------| -Test nr. 5: prova centesimi, verifico solo loutput corretto con pi dati in centesimi dati in input file:test5.txt sedea sedeb 1000 sedea sedeZ 100.5 sedeZ sedeK 200.7 ./mincost test5.txt Il collegamento pi breve tra |sedeZ| e |sedea| ha costo |100.50| Il collegamento pi breve tra |sedeK| e |sedeZ| ha costo |200.70| Il collegamento pi breve tra |sedeK| e |sedea| ha costo |301.20| Il collegamento pi breve tra |sedeb| e |sedea| ha costo |1000.00| Il collegamento pi breve tra |sedeZ| e |sedeb| ha costo |1100.50| Il collegamento pi breve tra |sedeK| e |sedeb| ha costo |1301.20| |----------------fine lista raggiunta ------------------|

Pag. 18

Progetto Algoritmi e Strutture Dati

-Test nr. 6: test dati intensivo


dati in input file:test4.txt a b 4 b c 5 z e 7 z a 2 z f 1 k f 2 u y 3 r t 8 k l 10 d e 1 d r 2 d h 15 d b 20 y d 4 q l 5 q v 6 q b 5 q c 9 q z 18 w1 w2 1

./mincost test4.txt Il collegamento pi breve tra |w2| e |w1| ha costo |1.00| Il collegamento pi breve tra |d| e |e| ha costo |1.00| Il collegamento pi breve tra |f| e |z| ha costo |1.00| Il collegamento pi breve tra |z| e |a| ha costo |2.00| Il collegamento pi breve tra |k| e |f| ha costo |2.00| Il collegamento pi breve tra |d| e |r| ha costo |2.00| Il collegamento pi breve tra |k| e |z| ha costo |3.00| Il collegamento pi breve tra |y| e |u| ha costo |3.00| Il collegamento pi breve tra |r| e |e| ha costo |3.00| Il collegamento pi breve tra |f| e |a| ha costo |3.00| Il collegamento pi breve tra |d| e |y| ha costo |4.00| Il collegamento pi breve tra |b| e |a| ha costo |4.00| Il collegamento pi breve tra |q| e |l| ha costo |5.00| Il collegamento pi breve tra |c| e |b| ha costo |5.00| Il collegamento pi breve tra |k| e |a| ha costo |5.00|

Pag. 19

Progetto Algoritmi e Strutture Dati

Il collegamento pi breve tra |q| e |b| ha costo |5.00| Il collegamento pi breve tra |y| e |e| ha costo |5.00| Il collegamento pi breve tra |z| e |b| ha costo |6.00| Il collegamento pi breve tra |r| e |y| ha costo |6.00| Il collegamento pi breve tra |v| e |q| ha costo |6.00| Il collegamento pi breve tra |f| e |b| ha costo |7.00| Il collegamento pi breve tra |d| e |u| ha costo |7.00| Il collegamento pi breve tra |e| e |z| ha costo |7.00| Il collegamento pi breve tra |f| e |e| ha costo |8.00| Il collegamento pi breve tra |d| e |z| ha costo |8.00| Il collegamento pi breve tra |t| e |r| ha costo |8.00| Il collegamento pi breve tra |u| e |e| ha costo |8.00| Il collegamento pi breve tra |c| e |a| ha costo |9.00| Il collegamento pi breve tra |r| e |u| ha costo |9.00| Il collegamento pi breve tra |q| e |c| ha costo |9.00| Il collegamento pi breve tra |d| e |f| ha costo |9.00| Il collegamento pi breve tra |k| e |b| ha costo |9.00| Il collegamento pi breve tra |e| e |a| ha costo |9.00| Il collegamento pi breve tra |q| e |a| ha costo |9.00| Il collegamento pi breve tra |l| e |k| ha costo |10.00| Il collegamento pi breve tra |d| e |a| ha costo |10.00| Il collegamento pi breve tra |k| e |e| ha costo |10.00| Il collegamento pi breve tra |r| e |z| ha costo |10.00| Il collegamento pi breve tra |d| e |t| ha costo |10.00| Il collegamento pi breve tra |l| e |b| ha costo |10.00| Il collegamento pi breve tra |v| e |b| ha costo |11.00| Il collegamento pi breve tra |z| e |c| ha costo |11.00|

Pag. 20

Progetto Algoritmi e Strutture Dati

Il collegamento pi breve tra |q| e |z| ha costo |11.00| Il collegamento pi breve tra |r| e |f| ha costo |11.00| Il collegamento pi breve tra |v| e |l| ha costo |11.00| Il collegamento pi breve tra |t| e |e| ha costo |11.00| Il collegamento pi breve tra |d| e |k| ha costo |11.00| Il collegamento pi breve tra |l| e |f| ha costo |12.00| Il collegamento pi breve tra |f| e |c| ha costo |12.00| Il collegamento pi breve tra |q| e |f| ha costo |12.00| Il collegamento pi breve tra |y| e |z| ha costo |12.00| Il collegamento pi breve tra |r| e |a| ha costo |12.00| Il collegamento pi breve tra |l| e |z| ha costo |13.00| Il collegamento pi breve tra |y| e |f| ha costo |13.00| Il collegamento pi breve tra |e| e |b| ha costo |13.00| Il collegamento pi breve tra |r| e |k| ha costo |13.00| Il collegamento pi breve tra |d| e |b| ha costo |14.00| Il collegamento pi breve tra |k| e |c| ha costo |14.00| Il collegamento pi breve tra |q| e |k| ha costo |14.00| Il collegamento pi breve tra |y| e |a| ha costo |14.00| Il collegamento pi breve tra |t| e |y| ha costo |14.00| Il collegamento pi breve tra |l| e |a| ha costo |14.00| Il collegamento pi breve tra |l| e |c| ha costo |14.00| Il collegamento pi breve tra |h| e |d| ha costo |15.00| Il collegamento pi breve tra |v| e |c| ha costo |15.00| Il collegamento pi breve tra |u| e |z| ha costo |15.00| Il collegamento pi breve tra |y| e |k| ha costo |15.00| Il collegamento pi breve tra |v| e |a| ha costo |15.00| Il collegamento pi breve tra |r| e |b| ha costo |16.00| Il collegamento pi breve tra |h| e |e| ha costo |16.00|

Pag. 21

Progetto Algoritmi e Strutture Dati

Il collegamento pi breve tra |u| e |f| ha costo |16.00| Il collegamento pi breve tra |v| e |z| ha costo |17.00| Il collegamento pi breve tra |u| e |a| ha costo |17.00| Il collegamento pi breve tra |t| e |u| ha costo |17.00| Il collegamento pi breve tra |h| e |r| ha costo |17.00| Il collegamento pi breve tra |e| e |c| ha costo |18.00| Il collegamento pi breve tra |q| e |e| ha costo |18.00| Il collegamento pi breve tra |u| e |k| ha costo |18.00| Il collegamento pi breve tra |y| e |b| ha costo |18.00| Il collegamento pi breve tra |v| e |f| ha costo |18.00| Il collegamento pi breve tra |t| e |z| ha costo |18.00| Il collegamento pi breve tra |d| e |c| ha costo |19.00| Il collegamento pi breve tra |q| e |d| ha costo |19.00| Il collegamento pi breve tra |t| e |f| ha costo |19.00| Il collegamento pi breve tra |h| e |y| ha costo |19.00| Il collegamento pi breve tra |l| e |e| ha costo |20.00| Il collegamento pi breve tra |v| e |k| ha costo |20.00| Il collegamento pi breve tra |t| e |a| ha costo |20.00| Il collegamento pi breve tra |u| e |b| ha costo |21.00| Il collegamento pi breve tra |d| e |l| ha costo |21.00| Il collegamento pi breve tra |q| e |r| ha costo |21.00| Il collegamento pi breve tra |r| e |c| ha costo |21.00| Il collegamento pi breve tra |t| e |k| ha costo |21.00| Il collegamento pi breve tra |h| e |u| ha costo |22.00| Il collegamento pi breve tra |l| e |r| ha costo |23.00| Il collegamento pi breve tra |y| e |c| ha costo |23.00| Il collegamento pi breve tra |q| e |y| ha costo |23.00|

Pag. 22

Progetto Algoritmi e Strutture Dati

Il collegamento pi breve tra |h| e |z| ha costo |23.00| Il collegamento pi breve tra |v| e |e| ha costo |24.00| Il collegamento pi breve tra |h| e |f| ha costo |24.00| Il collegamento pi breve tra |t| e |b| ha costo |24.00| Il collegamento pi breve tra |l| e |y| ha costo |25.00| Il collegamento pi breve tra |h| e |a| ha costo |25.00| Il collegamento pi breve tra |v| e |d| ha costo |25.00| Il collegamento pi breve tra |h| e |t| ha costo |25.00| Il collegamento pi breve tra |u| e |c| ha costo |26.00| Il collegamento pi breve tra |q| e |u| ha costo |26.00| Il collegamento pi breve tra |h| e |k| ha costo |26.00| Il collegamento pi breve tra |v| e |r| ha costo |27.00| Il collegamento pi breve tra |l| e |u| ha costo |28.00| Il collegamento pi breve tra |h| e |b| ha costo |29.00| Il collegamento pi breve tra |q| e |t| ha costo |29.00| Il collegamento pi breve tra |t| e |c| ha costo |29.00| Il collegamento pi breve tra |v| e |y| ha costo |29.00| Il collegamento pi breve tra |l| e |t| ha costo |31.00| Il collegamento pi breve tra |v| e |u| ha costo |32.00| Il collegamento pi breve tra |h| e |c| ha costo |34.00| Il collegamento pi breve tra |q| e |h| ha costo |34.00| Il collegamento pi breve tra |v| e |t| ha costo |35.00| Il collegamento pi breve tra |h| e |l| ha costo |36.00| Il collegamento pi breve tra |v| e |h| ha costo |40.00| |----------------fine lista raggiunta ------------------|


-Test nr. 7: verifica errori dati in input dati in input file:test6.txt a a 10 b b 10

Pag. 23

Progetto Algoritmi e Strutture Dati

./mincost test6.txt Trovati valori errati, la coppia a e a con valore 10.00 stata scartata Trovati valori errati, la coppia b e b con valore 10.00 stata scartata |----------------fine lista raggiunta ------------------| -Test nr. 8: verifica errori dati in input dati in input file:test7.txt a b 10 a a 20 z g -10 ./mincost test7.txt Trovati valori errati, la coppia a e a con valore 20.00 stata scartata Trovati valori errati, la coppia z e g con valore -10.00 stata scartata Il collegamento pi breve tra |b| e |a| ha costo |10.00| |----------------fine lista raggiunta ------------------| -Test nr. 9: verifica dati in un grafo fortemente connesso. Prima di procedere alla verifica utile sapere cosa aspettarsi. Se il mio grafo fortemente connesso composto da V elementi allora il numero di righe che mi dovr aspettare in output (V^2-V) / 2. Nel caso proposto ho V=4 quindi il numero di righe sar di (16-4)/2=6 che rappresenta tutti i cammini possibili. Quindi mi aspetter 6 righe. dati in input file:test8.txt a b 1 a c 2 a d 3 b c 4 b d 5 c d 6 ./mincost test8.txt Il collegamento pi breve tra |b| e |a| ha costo |1.00| Il collegamento pi breve tra |c| e |a| ha costo |2.00| Il collegamento pi breve tra |d| e |a| ha costo |3.00| Il collegamento pi breve tra |c| e |b| ha costo |3.00| Il collegamento pi breve tra |d| e |b| ha costo |4.00| Il collegamento pi breve tra |d| e |c| ha costo |5.00|

Pag. 24

Progetto Algoritmi e Strutture Dati

|----------------fine lista raggiunta ------------------| -Test nr. 10: verifica dati nel caso in cui la componente connessa sia rappresentata solo dalla coppia. In input abbiamo 4 coppie di valori scollegate dalle altre, quindi dovremmo aspettarci un output di 4 linee corrispondenti alle distanze minime dellinput. dati in input file:test9.txt a b 5 c d 10 e f 15 g h 20 ./mincost test9.txt Il collegamento pi breve tra |b| e |a| ha costo |5.00| Il collegamento pi breve tra |d| e |c| ha costo |10.00| Il collegamento pi breve tra |f| e |e| ha costo |15.00| Il collegamento pi breve tra |h| e |g| ha costo |20.00| |----------------fine lista raggiunta ------------------|

Pag. 25

Progetto Algoritmi e Strutture Dati

6. Valutazione della complessit


La valutazione della complessit viene prima focalizzata singolarmente sulle seguenti funzioni : funzione di crezione della lista di adiacenza definita come controlla_aggiungi_sedi(); funzione alloca_matrice_nuovo_elemento(); funzione di setting di matrice setta_matrice(); funzione dinserimento dati in matrice tramite lista di adiacenza definito con la funzione crea_matrice_da_grafo(); funzione dimplementazione dellalgoritmo di Floyd-Warshall ( matrice di adiacenza ); generazione dellarray di distanze minime dalla matrice implementato con la funzione genera_array_distanze_minime() funzione heapsort() funzioni di stampa vettore e di rilascio memoria. Per poter effettuare un calcolo della complessit del programma, le funzioni sopra esposte verranno prima trattate separatamente per poi poter essere inquadrate nel calcolo generale nel main(). Per quanto riguarda lutilizzo di funzioni appartenenti alle librerie standard, per semplicit di calcolo, in quanto non si conoscono dettagli sulla loro struttura, seppur possano essere queste studiate direttamente delle librerie, sempre stato assunto un valore pari a 1 ( es. malloc, strcpy, strcmp, printf ) infine viene omesso il calcolo della complessit per il controllo di errore in caso di errata allocazione della memoria, in quanto il programma, in tal caso, stato impostato per uscire dalla sua esecuzione. Lo studio stato effettuato riportando tutte le funzioni rispetto ad un numero di sedi n in input da file, valore al numero di vertici del grafo ed al numero di righe o colonne della matrice quadrata di adiacenza associata. Altra variabile impiegabile nel calcolo della complessit per le funizioni che prendono dati in input dopo lapplicazione dellalgoritmo di Floy- Warshall, derivante dal numero di collegamenti t che ci sono tra le sedi, ma nel caso peggiore, come gi spiegato in precedenza, t=(n^2-n)/2 quindi anche questa variabile stata riportata in funzione di n per poter effettuare un calcolo omogeneo. Funzione controlla_aggiungi_sedi(). Questa funzione, per operare richiama le funzioni di cerca_elemento_lista, aggiungi_elemento e di aggiungi_arco, infatti per poter aggiungere i nuovi dati devo sapere se lelemento gi esiste per poterlo eventualmente creare e poter poi aggiungere larco con il peso diretto acquisito da file. La funzione di cerca_elemento_lista() ha nel caso ottimo complessit O(1) che si verifica qualora lelemento sia immediatamente trovato o la lista sia vuota, nel caso pessimo la complessit risulta T(n)= 1+1+(n)(1+1+1)+1=3n+3=O(n), la condizione si verifica se tutti gli elementi della lista dovessero essere controllati in quanto lelemento nuovo. La funzione aggiungi_element() qualora dovesse essere richiamata per inserire il nuovo elemento avrebbe una complessit costante pari a T(n)=1+1+1+1+1+1+1+1=8, quindi la complessita risultante di ordine costante O(1). La funzione aggiungi_arco() alla stregua di aggiungi elemento ha complessit nel caso pessimo pari a T(n)=1+1+3+1+2=7 e risulta di ordine costante O(1). La funzine controlla_aggiungi_sedi() ha quindi una complessi nel caso pessimo veicolata dallutilizzo della funzione di ricerca, il caso in cui entrambi gli elementi della coppia acquisita non siano stati trovati e debbano essere inseriti come nuovi elementi. La complessit pessima calcolata risulta T(n)=1+1+(1+1+T(n)+8+1+1)+(1+1+T(n1)+8+1+1)+2*(7)+1, dove T(n) e T(n1) sono le complessit della funzione di cerca_elemento_lista() sopradescritta. Se consideriamo il caso in cui T(n) e T(n1) hanno entrambi complessit pessima, allora n1=n+1, che si verifica quando sia la prima

Pag. 26

Progetto Algoritmi e Strutture Dati

che la seconda ricerca danno esito negativo ed entrambi gli elementi sono da aggiungere alla lista in quanto nuovi. Il risultato della funzione nel caso pessimo, effettuando la sostituzione n1=n+1 sar dunque T(n)=6n+50. Quindi controlla_aggiungi_sedi(), chiamata singolarmente, risulterebbe di complessit asintotica pari a O(n) e strettamente collegata con il numero totale di sedi differenti trovate nel file. Funzione alloca_matrice_nuovo_elemento() Tale funzione, che serve per allocare una matrice di n elementi, nuovamente proporzionale a n, ossia al numero di vertici distinti appartenenti al grafo, in quanto per ogni ciclo for viene allocato un intero blocco corrispondente a n elementi. Il ciclo for viene iterato per n volte, quindi risulta che T(n)=1+(n)*(1+1+1)+1=3n+2. Tale funzione che viene chiamata tre volte per lallocazione dei dati, ha comunque una complessit asintotica pari a O(n). Funzione setta_matrice() La funzione costituita da due cicli for annidiati che devono settare gli elementi della matrice quadrata. Il calcolo della complessit risulta T(n)=1+n(1+n(1+1+1+1)+1+1)=4n^2+3n+1 =O(n^2) , con n sedi. Funzione crea_matrice_da_grafo() La funzione ha lo scopo di attraversare la lista di adiacenza per poter cos creare la matrice di adiacenza. Sul calcolo della complessit di questa funzione giocano due fattori: il numero di vertici e la quantit di archi che ha ogni vertice. Tuttavia ogni vertice n che appartenga allinsieme di vertici V pu avere al massimo V-1 archi di collegamento. Il caso peggiore quindi sar intuitivamente rappresentato dalla massimizzazione del numero di archi per ogni vertice. Con n vertici in input avremo una complessit asintotica che tender a n*(n-1) e quindi pari a O(n^2)). Dal calcolo del caso pessimo, in cui il numero di archi sia pari (n-1) vertici, risulta T(n)=1+1+n(1+(n-1)(1+1+1))= 3n^2- 2n+2=O(n^2) come da ipotesi intuitiva. Il caso ottimo invece si verifica quando ogni vertice connesso solo con un altro vertice e con nessun altro, il che trasformerebbe la precedente in T(n)=1+1+n(1+1*2)=3n+2=O(n). Quindi se i vertici sono poco connessi, abbiamo una crescita lineare allaumentare dei vertici, se i vertici sono fortemente connessi allora abbiamo una complessit con crescita quadratica. Funzione floyd-warshall()

La funzione che stata utilizzata quella della lezione 07.09, in cui vengono modificati alcuni aspetti in modo che possa essere adattata al caso specifico. La funzione originale poteva acquisire anche numeri negativi, questa stata modificata per poter utilizzare il numero -1 come valore rappresentante linfinito. Inoltre la funzione restituisce il numero di coppie collegate che sono state riscontrate, i dato servir poi per altre funzioni descritte in seguito. La complessit asintotica per un numero , derivante dallannidiamento di tre cicli for pari a O(n^3). Allinterno dellintero programma la complessit che ha un maggior peso, quindi risulter alla fine larmonica dominante nel calcolo generale. Lalgoritmo della funzione diviso in due parti principali. La prima parte costituita da due cicli for annidiati in cui la matrice di lavoro viene copiata nella matrice delle distanze e la matrice dei padri viene settata. La complessit per questa parte T(n)=1+(n)(1+1+(n)(1+1+1+1+1)+1)=5n^2- 10n+2=O(n^2). Quasto risultato va poi a sommarsi allalgoritmo vero e proprio di determinazione delle distanze minime che ha una complessit nel caso pessimo pari a: T(n)=1+(n)(1+1+(n)(1+1+(n)(1+1+1+1+1+1+1+1+1+1+1)))+1+1= 11n^3+2n^2+2n+3=O(n^3). Quindi consideriamo la complessit asintotica di tutta la funzione pari al valore O(n^3) ossia cubica.

Pag. 27

Progetto Algoritmi e Strutture Dati

Funzione genera_array_distanze_minime() La funzione ha lo scopo di riempire il vettore di strutture in modo non ordinato, per poter assolvere il compito viene attraversata la lista di adiacenza con due cicli for annidiati, tuttavia il secondo ciclo parte dallelemento a cui si fermato il ciclo pi esterno. Questo procedimento d origine a n*n/2 interazioni, nel ciclo pi interno viene infatti calcolata la media di iterazioni effettuate. Intuitivamente la complessit asintotica sar quindi pari a O(n^2) mentre il procedimento porta a prendere in considerazione esattamente met della matrice delle distanze, infatti si gi precedentemente spiegato che laltra met simmetrica. Effettuando il calcolo otteniamo T(n)=1+1+1+1+2+n(1+1+2+n/2(1+1+6))+1 = 4n^2+4n+7=O(n^). Funzione heapsort() Lalgoritmo di heapsort di tipo non stabile, ma che opera sul posto, non dipende dalla posizione dei dati iniziali e loperazione di setacciamento proporzionale allaltezza dellalbero binario associatavi. Usando la soluzione proposta nella lezione 04.11 attinente lheapsort, possiamo osservare il calcolo della complessit. Poich Il tempo di settaciamento dellheap proporzionale allaltezza dellalbero binario ad esso associato, dato che tale albero bilanciato, il valore sar O(log t). Il calcolo della complessit pu essere applicato al programma, bisogna tenere per in considerazione che se per scambiare un valore con lheap fatto di interi ho un costo unitario, nel caso del vettore di strutture che contiene i due nomi delle sedi nonch il costo minimo di trasporto, devono essere scambiati tutti e tre gli elementi. La valutazione asintotica dellalgoritmo O(t*log t). Dal calcolo risulta T(t)=1+2+(t/2)(1+log t +1)+1+1+(t-1)(1+3+3+3+log t +1)+1=1.5 t log t+12t-5 pari quindi a O(t log t). Per massimizzare t applico t=(n^2-n)/2 da cui la complessit pessima in funzione di n diventa T(n)=1.5*(n^2-n)/2 * log (n^2-n)/2 + 12 (n^2-n)/2 5 =O(n^2*log n^2) Funzioni di stampa vettore di rilascio memoria La funzione che si occupa della stampa dei risultati la funzione stampa_lista_coppie(), costituita da un semplice ciclo for che effettua lattraversamento del vettore. Il vettore formato da t coppie che nel caso pessimo sono pari a (n^2-n)/2 vertici. Se la complessit parziale dellalgoritmo derivante dal ciclo for di T(t)=1+t(1+1+1+1)=4t+1=O(t). Massimizzando il risultato in funzione dei vertici n possiamo porre che t=(n^2-n)/2 da cui 2n^2-2n +1 =O(n^2). Sono state implementate tre funzioni per liberare la memoria occupata dalla lista di adiacenza, dalle matrici e dal vettore di strutture. La funzione rilascia_grafo() ha complessit proporzionale al numero di vertici da rilasciare ed al numero di archi per ogni vertice. Considerando il caso pessimo, in cui ogni vertice n collegato tramite n-1 archi a tutti gli altri vertici, allora abbiamo che T(n)= n(1+(n-1)(1+1+1)+1)=3n^2- 1=O(n^2). La funzione rilascia_matrice() ha complesit proporzionale a n, in quanto tutto il blocco di n elementi viene rilasciato n volte. Da calcolo risulta T(n)=1+n(1+1+1)+1=3n+2=O(n). La funzione rilascio_vett_strutt() rilascia il vettore di strutture ed ha complessit pari a T(t)=1+t(2+1+1)+1=4t+2=O(t), come per la funzione di stampa la funzione massimizzata nel caso in cui n=(n^2-n)/2 da cui 4(n^2-n)/2+2=2n^2-2n+2=O(n^2) Conclusioni per il calcolo della complessit generale Tenento conto nel calcolo generale di una complessit pessima in cui ognuno degli n vertici in input collegato con gli altri (n-1) vertici, in cui quindi ogni vertice possiede (n-1) archi si proceduto allottenimento del risultato per successive sommatorie dei risultati delle singole funzioni. Viste le complessit parziali delle singole funzioni, il calcolo della complessit totale del programma , come precedentemente accennato, pesata principalmente dallalgoritmo di Floyd-Warshall, che quello che determina la complessit di grado maggiore essendo asintoticamente pari a O(n^3). -

Pag. 28

Progetto Algoritmi e Strutture Dati

Questo valore, come visibile dal calcolo, determina quindi il valore della complessit asintotica totale nel caso pessimo. Per poter calcolare un valore pi preciso di complessit nel caso pessimo, utile per poter derivare il limite superiore di tempo che lalgoritmo impiegher per risolvere lesecuzione, possiamo procedere allanalisi dei risultati parziali effettuando una sommatoria dei risultati. Nome funzione Complessit pessima relativa Controlla_aggiungi_sedi() = 6n+50 Alloca_matrice_nuovo_elemento() = 3*(3n+2) (*3 in quanto usata tre volte) Setta_matrice() = 4n^2+3n+1 Crea_matrice_da_grafo() = 3n^2-2n+2 Floyd_Warshall() = 11n^3+2n^2+2n+3 Genera_array_distanze_minime() = 4n^2+4n+7 Heapsort() = 1.5 t log t+12t-5 ( con t=(n^2-n)/2) Stampa_lista_coppie() = 2n^2-2n +1 Rilascia_matrice() = 3*(3n+2) (*3 in quanto usata tre volte) Rilascia_grafo() = 3n^2-1 Rilascia_vett_strutt() = 2n^2-2n+2 Nel main() listruzione controlla_aggiungi_sedi () viene iterata K volte con un ciclo while() che ha come condizione di uscita lultima riga letta da file. Literazione della righa K-esima deve comunque essere <=n^2 sedi differenti. Intuitivamente possiamo pensare che se gli elementi devono essere acquisiti per generare una matrice quadrata n*n allora la complessit pessima per poterlo fare deve essere proporzionale al massimo numero di elementi da acquisire, che poi non sono altro che i dati di ogni singola linea letta, quindi pari a O(n^2). Le K iterazioni che hanno una valenza pessima nel calcolo della complessit, sono solo quelle che creano vertici nuovi, ossia quelle che nella reiterazione trovano delle nuove sedi, le rimanenti iterazioni servono per linserimento di costi in un grafo gi parzialemnte definito. Quindi per il calcolo della complessit generale nel caso pessimo, possiamo procedere sommando le complessit parziali e i costi unitari restanti, riportando tutto in funzione del numero di vertici trovati: T(n)= 1+1+1+1+n*(1+(6n+50))+ 3*(3n+2)+ (4n^2+3n+1)+ (3n^2-2n+2) + (11n^3+2n^2+2n+3)+ (4n^2+4n+7)+ (1.5*(n^2-n)/2 * log (n^2-n)/2 + 12 (n^2-n)/2 5) + (2n^2-2n +1)+ 3*(3n+2)+ (3n^2-1)+ (2n^2-2n+2)+1 = 11n^3 + (1.5n^2-1.5n)/2+log(n^2-n)/2+32n^2+66n+27 =O(n^3) da cui risulta O(n^3) con n numero di vertici del grafo, pari anche al numero di righe o colonne della matrice quadrata generata.

Pag. 29