Ferramentas de Utilizador

Ferramentas de Site


inf05016:2015-2-trabalhos

Esta é uma versão antiga do documento!

Status das entregas (10/12/2015):

No. T1 T2 T3 T4 T5 T6
——–+—-+—-+—-+—-+—-+—-
21893 s s
174872
181098
193031 s
193276 s s s s s
193693 s s s s s s
194049 s
194636 s s s s s
195843 s s
219436
220640 s s s s s
228395 s s s s s
228450 s s s s s

Trabalho 1 (Heaps binários e algoritmo de Dijkstra)

Entrega: 17/08/2015

Objetivos

  • Implementar um heap n-ário.
  • Implementar o algoritmo de Dijkstra usando o heap implementado.
  • Determinar o valor de n tal que o algoritmo de Dijkstra com um heap n-ário tem o melhor desempenho.
  • Comparar a complexidade teórica pessimista com a complexidade real. Em particular verificar que a complexidade real respeita o limite teórico.
  • Avaliar o escalonamento do algoritmo Dijkstra.

Casos de teste

  • Verificação da complexidade do Dijkstra: usar o gerador de casos de testes abaixo.
  • Verificação do escalonamento: o caso de teste é o rede de trânsito de New York e dos EUA (distance), que pode ser baixado na página do DIMACS challenge.
  • Para testar em geral: Gerar um número suficiente (>30) de pares aleatórias de vértices origem e destino e medir o tempo de execução e o número de operações “insert”, “deletemin” e “decreasekey”.

Observações

  • Como o grafo possui 264346 vértices é necessário usar uma representação esparsa. Uma matriz de adjacência, em particular, não serve.

Convenções

  • As implementações do algoritmo de Dijkstra devem aceitar um grafo no formato da DIMACS challenge na entrada padrão (stdin), os índices de dois vértices origem e destino na linha de comando e imprimir o valor do caminho mais curto na saída padrão (stdout). Caso não existo caminho entre os dois vértices imprimir “inf”. Exemplo (em UNIX)
> ./dijkstra 1 2 < NY.gr
803

Gerador de casos de teste

/**
 * \file gen.cpp
 *   \author Marcus Ritt <mrpritt@inf.ufrgs.br> 
 *   \version $Id: emacs 2872 2009-01-31 01:46:50Z ritt $
 *   \date Time-stamp: <2011-08-24 15:17:49 ritt>
 */
 
#include <iostream>
#include <cassert>
using namespace std;
 
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
using namespace boost;
 
// information stored in vertices
struct VertexInformation {
  unsigned component;
};
 
// information stored in edges
struct EdgeInformation {
  unsigned weight;
};
 
const unsigned maxweight = 1000;
 
// graph is an adjacency list represented by vectors
typedef adjacency_list<vecS, vecS, directedS,VertexInformation,EdgeInformation> Graph;
typedef graph_traits<Graph>::vertex_descriptor Node;
typedef graph_traits <Graph>::edge_descriptor Edge;
 
int main(int argc, char *argv[]) {
  assert(argc == 3);
  unsigned n = atoi(argv[1]);
  double p = atof(argv[2]);
 
  srand48(time(0));
 
  // (1) generate random graph
  Graph g;
 
  for(unsigned i=0; i<n; i++)
    add_vertex(g);
 
  for(unsigned i=0; i<n; i++)
    for(unsigned j=0; j<n; j++)
      if (i != j && drand48() < p) {
        Edge e = add_edge(i,j,g).first;
	g[e].weight = lrand48()%maxweight;
      }
 
 
  // (2) print example path
  unsigned src = lrand48()%num_vertices(g);
  unsigned dst = lrand48()%num_vertices(g);
 
  vector<unsigned> dist(n);
  vector<unsigned> pred(n);
  dijkstra_shortest_paths(g,src,weight_map(get(&EdgeInformation::weight,g)).distance_map(&dist[0]).predecessor_map(&pred[0]));
  cerr << "Distance between " << src+1 << " and " << dst+1 << " is " << dist[dst] << endl;
 
  // (3) print out in DIMACS challenge format
  cout << "p sp " << num_vertices(g) << " " << num_edges(g) << endl;
  graph_traits<Graph>::edge_iterator eb, ee;
  for ( tie(eb, ee)=edges(g); eb != ee; eb++)
    cout << "a " << source(*eb,g)+1 << " " << target(*eb, g)+1 << " " << g[*eb].weight << endl;
}

Leitura (Exemplo)

void read_dimacs(std::istream& in, unsigned& n, unsigned& m, MyGraph& a) {
  std::string line="", dummy;
  while (line.substr(0,4) != "p sp")
    getline(in,line);
 
  // (1) get nodes and edges
  std::stringstream linestr;
  linestr.str(line);
  linestr >> dummy >> dummy >> n >> m;
  a.resize(n);
  unsigned i=0;
  while (i<m) {
    getline(in,line);
    if (line.substr(0,2) == "a ") {
      std::stringstream arc(line);
      unsigned u,v,w;
      char ac;
      arc >> ac >> u >> v >> w;
      // processar arco (u,v) com peso w
      i++;
    }
  }
}

Trabalho 2 (Union-find)

Entrega: 31/08/2015

Objetivos

  • Implementar a estrutura de dados “union-find” com balanceamento (UF1) e com balanceamento e compressão de caminhos (UF2).
  • Determinar a complexidade real (em comprimento de caminho) de uma sequência de operações e apresentar um histograma e a complexidade amortizada.
  • Conduzir um experimento com grafos randômicos usando UF para análise
    • Modificar o UF para poder determinar em O(1) o número de componentes conexos, e o tamanho de cada componente; caso tem k componentes deve ser possível determinar a tamanho de cada uma em tempo total O(k)
    • Conduzir experimentos como segue: criar um grafo vazio com n vertices, e inserir n/2 arestas aleatórias, e depois determinar o tamanho da componente maior
    • Repetir este experimento 30 vezes para cada n, e para n=2^i, i=1,…,20.
    • Apresentar um boxplot com o tamanho da maior componente em função de n, e uma regressão polinomial do tamanho da maior componente em função de n. Conclusão?

Casos de teste

  • Para o primeiro parte: gerar sequências randômicas de operações.
  • Para o segundo parte: usar um gerador de casos de testes abaixo.

Convenções

  • Fornecer uma implementação do UF que recebe na entrada padrão (stdin) uma lista de operações “make-set n”; “union n1 n2” e “find n” e imprime na saída padrão (stdout) os resultados das operações find.

Trabalho 3 (Fluxo s-t máximo)

Entrega: 21/09/2015

Objetivos

  • Implementar o algoritmo de Ford-Fulkerson (estratégia “caminho arbitrário” s-t) e Edmonds-Karp (estratégia do “caminho mais curto” s-t).
  • Verificar a complexidade dos dois algoritmos experimentalmente e comparar com a complexidade teorica O(mn(m+n))=O(m^2n).
  • Qual variante é mais eficiente na prática em termos de número iterações globais (caminhos buscados) e complexidade total (vértices expandidos)?

Casos de teste

  • Um gerador de casos de teste em formato DIMACS em C é disponível aqui.
  • Documentação:
             To use:  cc washington.c -o gengraph
                      gengraph function arg1 arg2 arg3

             Command line arguments have the following meanings:

                      function:           index of desired graph type
                      arg1, arg2, arg3:   meanings depend on graph type
                                          (briefly listed below: see code
                                           comments for more info)

                 Mesh Graph:          1 rows  cols  maxcapacity
                 Random Level Graph:  2 rows  cols  maxcapacity
                 Random 2-Level Graph:3 rows  cols  maxcapacity
                 Matching Graph:      4 vertices  degree
                 Square Mesh:         5 side  degree  maxcapacity
                 Basic Line:          6 rows  cols  degree
                 Exponential Line:    7 rows  cols  degree
                 Double Exponential   8 rows  cols  degree
                 DinicBadCase:        9 vertices
                      (causes n augmentation phases)
                 GoldBadCase         10 vertices
                 Cheryian            11 dim1 dim2  range
                      (last 2 are bad for goldberg's algorithm)
No. Nome Parâmetros Descrição n m
1 Mesh r,c Grade, 3 viz. 1 direita rc+2 3r(c-1)
2 Random level r,c Grade, 3 viz. rand. 1 direita rc+2 3r(c-1)
3 Random 2-level r,c Grade, 3 viz. rand. 2 direita rc+2 3r(c-1)
4 Matching n,d Bipart. n-n, d viz. rand. 2n+2 n(d+2)
5 Square Mesh d,D Quadr. mesh dxd, grau D d*d+2 (d-1)dD+2d
6 BasicLine n,m,D Linha, grau D nm+2 nmD+2m
7 ExpLine n,m,D Linha, grau D nm+2 nmD+2m
8 DExpLine n,m,D Linha, grau D nm+2 nmD+2m
9 DinicBad n Linha n 2n-3
10 GoldBad n 3n+3 4n+1

Convenções

  • As implementações do algoritmo devem aceitar uma instância no formato DIMACS na entrada padrão (stdin) e imprimir o valor do fluxo máximo na saída padrão (stdout).

Verificação

  • O seguinte código determina o fluxo máximo. Para compilar: Usar C++ e Boost.
/**
 * \file maxflow.cpp
 *   \author Marcus Ritt <mrpritt@inf.ufrgs.br>
 *   \version $Id: emacs 2872 2009-01-31 01:46:50Z ritt $
 *   \date Time-stamp: <2009-03-23 17:52:25 ritt>
 *
 * Read a maximum flow problem in DIMACS format and output the maximum flow.
 *
 */
#include <iostream>
#include <cstring>
using namespace std;
 
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/read_dimacs.hpp>
#include <boost/graph/edmonds_karp_max_flow.hpp>
using namespace boost;
 
// graph element descriptors
typedef adjacency_list_traits<vecS,vecS,directedS>::vertex_descriptor DiNode;
typedef adjacency_list_traits<vecS,vecS,directedS>::edge_descriptor Edge;
 
typedef unsigned Capacity;
struct VertexInformation {};
struct EdgeInformation {
  Capacity edge_capacity;
  Capacity edge_residual_capacity;
  Edge reverse_edge;
};
 
typedef adjacency_list<vecS,vecS,directedS,VertexInformation,EdgeInformation> DiGraph;
 
 
int main(int argc, char *argv[]) {
  // (0) read graph
 
  DiGraph g;
  DiNode s,t;
 
  read_dimacs_max_flow(g,
                       get(&EdgeInformation::edge_capacity,g),
                       get(&EdgeInformation::reverse_edge,g),
                       s, t);
 
  // (1) determine maximum flow
  cout << edmonds_karp_max_flow(g, s, t,
                                capacity_map(get(&EdgeInformation::edge_capacity,g)).
                                residual_capacity_map(get(&EdgeInformation::edge_residual_capacity,g)).
                                reverse_edge_map(get(&EdgeInformation::reverse_edge,g))) << endl;
}

Trabalho 4 (Open pit mining)

upload.wikimedia.org_wikipedia_commons_8_83_udachnaya_pipe.jpg

Entrega: 12/10/2015

Objetivos

  • Aplicar o algoritmo de fluxo máximo desenvolvido no trabalho 3 para resolver o problema de “open pit mining”
  • Uma descrição do problema está disponível aqui.
  • Apresentar as soluções das 6 instâncias de teste abaixo no relatório (como figuras).

Exemplo

Casos de teste

  • Um gerador de casos de teste em C++ é disponível aqui.
  • Documentação:
  Gerar uma instância aleatória:                ./opm --w 10 --h 10 --ins test.ins; display opm.pbm
  Visualizar uma solução:                       ./opm --ins test.ins --sol test.sol --pbm vis.pbm; display vis.pbm
  Converter PBM para PNG (Linux/ImageMagick):    convert vis.pbm vis.png

Convenções

  • As implementações do algoritmo devem aceitar uma instância na entrada padrão (stdin), imprimir a solução na saída padrão (stdout), e imprimir o lucro total na saíde de erro (stderr).
  • Formato da instância: uma largura w, uma altura h, e lucros pij, com -128 < pij < 128, e pij != 0:
  w h
  p11 p12 ... p1w
  p21 p22 ... p2w
      ...
  ph1 ph2 ... phw
  • Formato da solução: uma largura w, uma altura h, e booleanos sij que indicam se a celula ij é extraída ou não
  w h
  s11 s12 ... s1w
  s21 s22 ... s2w
      ...
  sh1 sh2 ... shw

Trabalho 5 (Emparelhamentos)

Entrega: 16/11/2015

Objetivos

  • Implementar o algoritmo Húngaro que resolve o problema do emparelhamento de maior peso em grafos bi-partidos ponderados.
  • Documentar a implementação, em particular as estruturas de dados para a representação do problema.
  • Conduzir testes, que demonstram que a complexidade do algoritmo é O(mn^2), usando Bellman-Ford para busca de caminhos aumentantes.

Casos de teste

  • Gerar grafos bi-partidos completos com pesos aleatórios escolhidos uniformemente no intervalo [0,n^2].
  • Casos de teste com soluções para a emparelhamento ponderado perfeito mínimo (para comparar com emparelhamento perfeito máximo: multiplicar os valores por -1). O formato corresponde com o que está descrito nas convenções abaixo com a última linha informando o peso total da solução correta.

Convenções

  • As implementações do algoritmo devem aceitar um grafo bi-partido não-direcionado completo na entrada padrão (stdin) e imprimir o maior peso de um emparelhamento na saída padrão (stdout). O formato é
n
p11 p12 p13 ... p1n
p21 p22 p23 ... p2n
...
pn1 pn2 pn3 ... pnn

com pij o peso entre vértice i do primeiro parte do grafo e vértice j do segundo parte.

Trabalho 6 (O algoritmo de Cristofides)

Entrega: 10/12/2015 (Observação: esta data não será prorrogada)

Objetivos

  • Implementar o algoritmo de Cristofides
  • Testar duas variantes: a) usando um emparelhamento perfeito máximo, b) usando um emparelhamento perfeito obtido por um algoritmo guloso que seleciona cada vez a aresta livre de maior peso.
  • Conduzir testes que avaliam o desempenho do algoritmo em termos de qualidade e tempo.

Casos de teste

  • Disponíveis na TSPLIB. Aplicar pelo menos nas instâncias gr96, gr202, gr229, gr431, ali535, gr666, dsj1000, pla7397, pla33810, pla85900.
  • Para avaliar a qualidade: relatar o desvio relativo percentual (v-b)/b de uma solução com valor v e melhor valor conhecido b.
  • Informar o tempo de execução em segundos.

Convenções

  • A implementação deve aceitar uma instância na entrada padrão (stdin) e imprimir o valor da rota obtida pelo algoritmo de Cristofides na saída padrão (stdout).
  • O formato das instâncias é o mesmo formato usado na TSPLIB: uma documentação está aqui

Materiais

  • Para calcular o matching, usar o software Blossom V. (O algoritmo Húngaro da questão 5 não serve, porque é necessário determinar um matching no grafo não-bipartido.)
  • Para facilitar a leitura de instâncias da TSPLIB, tspParse.H e tspParse.C mostram um exemplo em C++, que pode ser adaptado.
inf05016/2015-2-trabalhos.1449768163.txt.gz · Esta página foi modificada pela última vez em: 2015/12/10 15:22 por marcus