Università degli Studi di Napoli Parthenope Corso di Laurea in Informatica A.A 2014/15 PROGETTO PROGRAMMAZIONE III 40 Algoritmi sui Grafi Relatore: Prof. Raffaele Montella Studente: Diego Parlato Matricola: 0124/636
INTRODUZIONE al PROGETTO Lo scopo del progetto è quello di testare quaranta algoritmi sui grafi messi a disposizione su web dall università di Princeton. Gli algoritmi coprono i maggiori argomenti delle quattro tipologie di grafi. Ogni algoritmo dovrà essere testato per valori di 100, 1000, 10000. Si è scelto di far corrispondere questi numeri agli archi, visto che la maggior parte delle operazioni si basa sull aggiunta di questi ultimi. Per testare gli algoritmi si è creata un ambiente apposito di testing, sottoforma di classe, che conterrà anche il metodo main. Oltre al main, la classe AmbienteTesting ha tre metodi per ogni algoritmo. Ogni metodo testerà l algoritmo su una delle tre dimensioni di dati su cui vanno testati. I risultati ottenuti sono trascritti, grazie al package jxl, automaticamente in un file excel che sarà fornito alla consegna. E giusto avvertire che alcuni algoritmi prendono dei tempi decisamente lunghi (si arriva fino 1000 secondi circa), e altri terminano il Java Heap Space (almeno sul calcolatore usato per il testing). E quindi consigliabile o dividere il testing in trance, o avere un calcolatore parecchio performante. GRAFI IN GENERALE Un grafo è una coppia di elementi (V,E), dove V è un insieme di vertici, ed E un insieme di archi. Un arco è, in pratica, una linea che collega due vertici. Per convenzione nomiamo i vertici V da 0 a V-1, in questo modo rendiamo più semplice riferirci ad essi come indice di un array. Usiamo la notazione v-w(w-v) per fare riferimento ad un arco che connette due vertici v e w. Si sono testati algoritmi sulle quattro tipologie principali di grafi: Grafi Indiretti (con semplici connessioni senza direzione), Grafi Diretti (con semplici connessioni ma con direzione), Grafi Indiretti con peso (gli archi sono senza direzione, ma hanno un peso), Grafi diretti con peso (Gli archi hanno direzione e peso). Vediamo gli algoritmi che abbiamo testato per ognuna delle quattro categorie. Algoritmo Graph GRAFI INDIRETTI La classe Graph rappresenta un grafo indiretto di vertici V. Permette l operazione di aggiunta di un arco, il conoscere il numero di vertici e archi. Sono possibili selfloops. Per implementarlo sono usate liste di adiacenza. Tutte le operazioni prendono tempo costante nel caso peggiore.
Algoritmo GraphGenerator La classe GraphGenerator consentem tramite metodi static, la creazione di vari tipi di grafi (random, bipartite, ad albero, ecc.) Algoritmo DepthFirstSearch La classe DepthFirstSearch determina quali sono I vertici connessi a una data sorgente s. Il costruttore prende tempo V+E nel caso peggiore. Algoritmo DepthFirstPaths La classe DepthFirstPaths restituisce, se possibile, un percorso da una sorgente s ad ogni altro vertice del grafo. Per implementare viene usata la depth-first search. Il costruttore impiega V+E nel caso peggiore. Algoritmo BreadthFirstPaths La classe BreadthFirstPaths trova il percorso più breve (in numero di archi) tra una sorgente ed ogni altro nodo. Usa la breadth-first search. Il costruttore impiega tempo proporzionale a V+E. Algoritmo CC (Connected Components) La classe CC determina i component connessi in un grafo indiretto. Dà anche la possibilità di verificare se due vertici sono nello stessa component connessa. Una componente connessa è un sottografo in cui tra due qualsiasi vertici c è un cammino, e nessuno è collegato ai vertici degli altri sottografi. Questa classe usa la depth-first search. Algoritmo Bipartite Un grafo bipartito è un grafo tale che l'insieme dei suoi vertici si può partizionare in due sottoinsiemi tali che ogni vertice di una di queste due parti è collegato solo a vertici dell'altra. La classe Bipartite determina se un grafo è bipartite. Usa la depthfirst search. Algoritmo Cycle La classe Cycle determina se nel grafo è presente un ciclo. Se si, il metodo hascycle ritorna 1. Usa la depth-first search. Algoritmo SymbolGraph La classe SymbolGraph rappresenta un grafo indiretto dove i vertici sono etichettati da stringhe. Supporta l inizializzazione di un SymbolGraph da file.
Algoritmo DegreesOfSeparation La classe DegreesOfSeparation provvede a individuare la distanza tra un elemento sorgente e tutti gli altri. Un esempio di utilizzo è nei social network. Il tempo d esecuzione è proporzionale al numero di connessioni. Algoritmo Digraph GRAFI DIRETTI La classe Digraph rappresenta un grafo diretto di vertici V. Permette l operazione di aggiunta di un arco, il conoscere il numero di vertici e archi. Sono possibili selfloops. Per implementarlo sono usate liste di adiacenza. Tutte le operazioni prendono tempo costante nel caso peggiore. Algoritmo DigraphGenerator La classe DigraphGenerator consente tramite metodi static, la creazione di vari tipi di grafi diretti (random, bipartite, ad albero, ecc.) Algoritmo DirectedDFS La classe DirectedDFS determina quali sono I vertici connessi a una data sorgente s. Il costruttore prende tempo V+E nel caso peggiore. Algoritmo DepthFirstDirectedPaths La classe DepthFirstDirectedPaths restituisce, se possibile, un percorso da una sorgente s ad ogni altro vertice del grafo. Per implementare viene usata la depth-first search. Il costruttore impiega V+E nel caso peggiore. Algoritmo DirectedCycle La classe DirectedCycle determina se nel grafo è presente un ciclo. Se si, il metodo hascycle ritorna 1. Usa la depth-first search Algoritmo DepthFirstOrder La classe DepthFirstOrder determina l ordine dei vertici all interno di un digraph in preorder, postorder e inorder, usando il depth-first search. Il costruttore impiega tempo proporzionale a V+E. Algoritmo Topological
La classe Topological determina se un grafo ha un ordine topologico ( un ordinamento lineare di tutti i vertici). Un digraph ha ordine topologico solo se è un DAG(grafo aciclico diretto). Usa la depth-first search. Il costruttore prende tempo proporzionale a V+E. Algoritmo BreadthFirstDirectedPaths La classe BreadthDirectedFirstPaths determina il percorso più breve da un vertice sorgente a tutti gli altri vertici. Usa la breadth-first search. Algoritmo TransitiveClosure La classe TransitiveClosure computa la chiusura transitive per un digraph. L implementazione usa la depth-first search da ogni vertice. Algoritmo SymbolDigraph La classe Symboldigraph rappresenta un grafo indiretto dove i vertici sono etichettati da stringhe. Supporta l inizializzazione di un SymbolDigraph da file. Algoritmo KosarajuSharirSCC La classe KosarajuSharirSCC determina se un digraph è fortemente connesso. Un grafo orientato si dice fortemente connesso se esiste un cammino da v a w per ogni coppia v,w V. L implementazione usa l algoritmo Kosaraju-Sharir. Il costruttore prende tempo proporzionale a V+E. Algoritmo TarjanSCC La classe TarjanSCC determina se un digraph è fortemente connesso. Un grafo orientato si dice fortemente connesso se esiste un cammino da v a w per ogni coppia v,w V. L implementazione usa l algoritmo Tarjan. Il costruttore prende tempo proporzionale a V+E. Algoritmo GabowSCC La classe GabowSCC determina se un digraph è fortemente connesso. Un grafo orientato si dice fortemente connesso se esiste un cammino da v a w per ogni coppia v,w V. L implementazione usa l algoritmo Gabow. Il costruttore prende tempo proporzionale a V+E.
GRAFI INDIRETTI con PESO Questo tipo di grafi è tendenzialmente usato per il calcolo dell albero di copertura minimo. Algoritmo EdgeWeightedGraph La classe EdgeWeightedGraph rappresenta un grafo indiretto con peso di vertici V. Permette l operazione di aggiunta di un arco, il conoscere il numero di vertici e archi. Sono possibili self-loops. Per implementarlo sono usate liste di adiacenza. Tutte le operazioni prendono tempo costante nel caso peggiore. Algoritmo Edge La classe Edge crea un arco in un grafo indiretto con peso. Ogni arco consiste nello specificare due vertici. Algoritmo LazyPrimMST La classe LazyPrimMST calcola l albero minimo di copertura in un grafo non orientate con peso. Il peso può essere positivo o negativo. Se il grafo non è connesso, calcolerà la foresta di copertura minima. Il metodo weight() restituisce il peso minimo dell albero. Viene usato la versione lazy dell algoritmo di Prim. Il costruttore prende tempo proporzionale a E log E. Algoritmo PrimMST La classe PrimMST calcola l albero minimo di copertura in un grafo non orientate con peso. Il peso può essere positivo o negativo. Se il grafo non è connesso, calcolerà la foresta di copertura minima. Il metodo weight() restituisce il peso minimo dell albero. Viene usato l algoritmo di Prim. Il costruttore prende tempo proporzionale a E log V. Algoritmo KruskalMST La classe KruskalMST calcola l albero minimo di copertura in un grafo non orientate con peso. Il peso può essere positivo o negativo. Se il grafo non è connesso, calcolerà la foresta di copertura minima. Il metodo weight() restituisce il peso minimo dell albero. Viene usato l algoritmo di Prim. Il costruttore prende tempo proporzionale a E log V. Algoritmo BoruvkaMST La classe BoruvkaMST calcola l albero minimo di copertura in un grafo non orientate con peso. Il peso può essere positivo o negativo. Se il grafo non è connesso, calcolerà la foresta di copertura minima. Il metodo weight() restituisce il peso
minimo dell albero. Viene usato l algoritmo di Prim. Il costruttore prende tempo proporzionale a E log V. GRAFI DIRETTI con PESO Questo tipo di grafi è tendenzialmente usato per il calcolo del percorso minimo. Algoritmo EdgeWeightedDigraph La classe EdgeWeightedDigraph rappresenta un grafo indiretto con peso di vertici V. Permette l operazione di aggiunta di un arco, il conoscere il numero di vertici e archi. Sono possibili self-loops. Per implementarlo sono usate liste di adiacenza. Tutte le operazioni prendono tempo costante nel caso peggiore. Algoritmo DirectedEdge La classe DirectedEdge crea un arco in un grafo indiretto con peso. Ogni arco consiste nello specificare due vertici. Qui bisogna inserire la direzione. Algoritmo DijkstraSP La classe DijkstraSP determina il percorso minimo in un grafo orientato con peso dove I pesi degli archi sono non negative. Si usa ovviamente l implementazione dell algoritmo di Dijkstra. Il costruttore prende tempo proporzionale a E log V. Algoritmo AcyclicSP La classe AcyclicSP determina il percorso minimo da singola sorgente in un DAG. Il peso degli archi può essere positive, negative o zerio. L implementazione usa un algoritmo basato sull ordinamento topologico. Il costruttore prente tempo V+E. Algoritmo AcyclicLP La classe AcyclicLP determina il percorso più lungo da singola sorgente in un DAG. Il peso degli archi può essere positive, negative o zerio. L implementazione usa un algoritmo basato sull ordinamento topologico. Il costruttore prente tempo V+E. Algoritmo CPM (critical path method) La classe CPM risolve il problema del job scheduling. Riduce il problema alla risoluzione del problema del percorso più lungo in un DAG. Costruice un DAG dalle specifiche del problema dello job scheduling. Usa l algoritmo AcyclicLP. Il tempo d esecuzione è proporzionale a V+E.
Algoritmo BellmanFordSP La classe BellmanFordSP resolve il problema del calcolo del percorso minimo da sorgente singola in un grafo orientato con peso con nessun ciclo negativo. Il peso dell arco può essere positive, negative o zero. L algoritmo restituisce o il percorso minimo o informa che c è un ciclo negative. Usa l algoritmo Bellman-Ford-Moore. Il costruttore prende tempo proporzionale a V(V+E). Algoritmo EdgeWeightedDirectedCycle La classe EdgeWeightedDirectedCycle determina se un grafo orientato con peso ha un ciclo diretto o no. Se si, il metodo hascycle ritorna 1. Usa la depth-first search. Il costruttore prende tempo proporzionale a V+E. Algoritmo Arbitrage La classe Arbitrage class provvede a trovare ad un cliente un opportunità in una tabella di scambio valuta costruendo una rappresentazione della tabella in un grafo oreintato con peso. Trovando un ciclo negative nel grafo orientato, c è l opportunità di guadagno. L implementazione usa l algoritmo Bellman-Ford per trovare il ciclo negativo. Il tempo d esecuzione è proporzionale a V 3 Algoritmo FloydWarshall La classe FloydWarshall trova il percorso minimo tra tutte le coppie in un grafo orientato con peso che non ha ciclo negative. Il peso di un arco può essere positivo, negativo o zero. L algoritmo quindi restituisce o il percorso più breve per ogni coppia o un ciclo negative. L implementazione usa l algoritmo di Floyd-Warshall. Il costuttore prende tempo proporzionale a V 3 Algoritmo AdjMatrixEdgeWeightedDigraph La classe AdjMatrixEdgeWeightedDigraph rappresenta un grafo orientato con peso dove ogni arco è del tipo DirectedEdge. Supporta le due operazioni principali: aggiungere un arco e iterare per ogni vertice che si incontra partendo da una sorgente. Ha anche metodi per tornare numero di vertici e di archi. Sono permessi i self loops. L implementazione come rappresentazione una matrice di adiacenza. Tutte le operazioni richiedono tempo costante. CONCLUSIONI In generale si può concludere che, paragonando gli algoritmi che svolgono gli stessi compiti sui grafi orientati e sui grafi non orientati, gli algoritmi relativi ai primi tendono ad avere performance migliore. Ciò è aspettato, perché avendo gli archi una direzione, diminuiscono i percorsi e i collegamenti che ci sono tra i vertici.